123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579 |
- <template>
- <div class="config-container">
- <!-- 搜索区域 -->
- <div class="search-section">
- <div class="search-inputs">
- <el-input
- v-model="params.order_name"
- placeholder="搜索配置项名称"
- prefix-icon="el-icon-search"
- clearable
- @clear="searchOption"
- @input="searchOption"
- />
- <el-input
- v-model="params.keyword"
- placeholder="搜索商品名称"
- prefix-icon="el-icon-search"
- clearable
- @clear="searchOption"
- @input="searchOption"
- />
- </div>
- <el-button type="primary" @click="addOptionValues">新增配置项</el-button>
- </div>
- <!-- 配置项列表 -->
- <el-table :data="optionList.data" style="margin-top: 20px;">
- <el-table-column prop="name" label="配置项名称" width="160"/>
- <el-table-column prop="type" label="类型" width="110">
- <template slot-scope="scope">
- <el-tag :type="getTypeTag(scope.row.type)">
- {{ getTypeLabel(scope.row.type) }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="values" label="可选内容">
- <template slot-scope="scope">
- <span v-if="scope.row.option_values && scope.row.option_values.length">
- <span v-for="(v, idx) in scope.row.option_values.filter(v => v.status !== 0)" :key="idx">
- <el-tag size="small" type="info">{{ v.value }}</el-tag>
- <el-tag v-if="v.material_code" size="small" type="warning" style="margin-left:2px;">({{ v.material_code }})</el-tag>
- <span v-if="idx !== scope.row.option_values.filter(v => v.status !== 0).length - 1"> / </span>
- </span>
- </span>
- <span v-else>—</span>
- </template>
- </el-table-column>
- <el-table-column label="适用商品" min-width="200">
- <template slot-scope="scope">
- <div v-if="scope.row.show_product_names && scope.row.show_product_names.length > 0">
- <el-tag
- v-for="(productName, index) in scope.row.show_product_names"
- :key="index"
- style="margin-right: 5px; margin-bottom: 5px; cursor: pointer;"
- @click="handleProductTagClick(productName)"
- closable
- @close="handleProductTagClose(scope.row, index)"
- v-show="index < 6 || scope.row.productsExpanded"
- >
- {{ productName }}
- </el-tag>
- </div>
- <div class="product-actions">
- <el-button
- v-if="scope.row.show_product_names && scope.row.show_product_names.length > 6"
- type="text"
- size="mini"
- @click="toggleProductsExpanded(scope.row)"
- style="padding: 0; margin-right: 10px;">
- {{ scope.row.productsExpanded ? '收起' : `查看更多(${scope.row.show_product_names.length - 6})` }}
- </el-button>
- <el-button v-if="goodId" type="text" size="mini" @click="setProductAPI(scope.row)">配置此商品</el-button>
- <el-button v-if="!goodId" type="text" size="mini" @click="showProductSelector(scope.row)">选择商品</el-button>
- </div>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="180">
- <template slot-scope="scope">
- <el-button size="mini" @click="editOption(scope.row)">编辑</el-button>
- <el-button size="mini" type="danger" @click="deleteOption(scope.row)">删除</el-button>
- </template>
- </el-table-column>
- </el-table>
- <!-- 新增/编辑配置项弹窗 -->
- <el-dialog :title="editMode ? '编辑配置项' : '新增配置项'" :visible.sync="showAddDialog" width="50%">
- <el-form :model="optionForm" label-width="120px" class="option-form">
- <el-form-item label="配置项名称" prop="name">
- <el-input v-model="optionForm.name" style="width: 400px;"/>
- </el-form-item>
- <el-form-item label="类型" prop="type">
- <el-select v-model="optionForm.type" style="width: 400px;">
- <el-option label="单选" value="radio"/>
- <el-option label="多选" value="checkbox"/>
- <el-option label="文本" value="text"/>
- </el-select>
- </el-form-item>
- <el-form-item label="可选内容" v-if="optionForm.type === 'radio' || optionForm.type === 'checkbox'" prop="option_values">
- <div class="option-values">
- <div v-for="(item, idx) in optionForm.option_values" :key="idx" class="option-item">
- <el-input v-show="item.status"
- v-model="optionForm.option_values[idx].value"
- placeholder="输入选项"
- style="width: 180px; margin-right: 10px;"
- />
- <el-input v-show="item.status"
- v-model="optionForm.option_values[idx].material_code"
- placeholder="物料编号"
- style="width: 180px; margin-right: 10px;"
- />
- <el-button
- v-show="item.status"
- icon="el-icon-delete"
- @click="removeOption(item, idx)"
- type="danger"
- ></el-button>
- </div>
- <el-button size="mini" type="primary" @click="optionForm.option_values.push({value: '', material_code: '', status: 1})">
- <i class="el-icon-plus"></i> 添加选项
- </el-button>
- </div>
- </el-form-item>
- </el-form>
- <div slot="footer">
- <el-button @click="showAddDialog = false">取消</el-button>
- <el-button type="primary" @click="saveOption">保存</el-button>
- </div>
- </el-dialog>
- <!-- 选择商品弹窗 -->
- <el-dialog title="选择适用商品" :visible.sync="showProductSelectorDialog" width="70%">
- <div class="product-selector">
- <div class="search-section">
- <el-input
- v-model="productSearchQuery"
- placeholder="搜索商品名称"
- prefix-icon="el-icon-search"
- clearable
- @clear="handleProductSearch"
- @input="handleProductSearch"
- style="width: 300px; margin-right: 10px;"
- />
- </div>
- <div class="product-list">
- <el-table :data="filteredProducts.data" style="width: 100%" height="400" @selection-change="handleProductSelectionChange" v-loading="loading">
- <el-table-column type="selection" width="55"></el-table-column>
- <el-table-column prop="name" label="商品名称" min-width="200"></el-table-column>
- <el-table-column label="是否可用" prop="enabled" width="100">
- <template slot-scope="scope">
- <el-tag :type="scope.row.enabled ? 'success' : 'danger'" size="mini" effect="dark">
- {{ scope.row.enabled ? '可用' : '不可用' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="no" label="生产型号" width="160"></el-table-column>
- <el-table-column prop="type" label="类型" width="120"></el-table-column>
- </el-table>
- <el-pagination
- v-if="filteredProducts"
- slot="pagination"
- @size-change="handleSizeChange"
- @current-change="handleCurrentChange"
- :current-page="filteredProducts.page_no"
- :page-sizes="[20, 50, 100, 200]"
- :page-size="filteredProducts.page_size"
- layout="total, sizes, prev, pager, next, jumper"
- :total="filteredProducts.data_total">
- </el-pagination>
- </div>
- </div>
- <div slot="footer">
- <el-button @click="showProductSelectorDialog = false">取消</el-button>
- <el-button type="primary" @click="saveProductSelection">确定</el-button>
- </div>
- </el-dialog>
- <el-pagination
- v-if="optionList"
- slot="pagination"
- @size-change="handlePageSizeChange"
- @current-change="handlePageCurrentChange"
- :current-page="optionList.page_no"
- :page-sizes="[20, 50, 100, 200]"
- :page-size="optionList.page_size"
- layout="total, sizes, prev, pager, next, jumper"
- :total="optionList.data_total">
- </el-pagination>
- </div>
- </template>
- <script>
- import * as API_goods from '@/api/goods'
- import * as API_configOptions from '@/api/pjConfigOptions'
- export default {
- name: 'pjGoodsList',
- props: {
- goodId: {
- type: Number,
- default: 0
- },
- goodName: {
- type: String,
- default: ''
- }
- },
- data() {
- return {
- products: [],
- optionList: [],
- searchQuery: '',
- showAddDialog: false,
- showProductSelectorDialog: false,
- editMode: false,
- optionForm: {
- id: null,
- name: '',
- type: 'radio',
- option_values: [{ value: '', material_code: '', status: 1 }],
- productIds: []
- },
- currentEditingOption: null,
- selectedProductIds: [],
- selectedProductNames: [],
- productSearchQuery: '',
- filteredProducts: [],
- loading: false,
- checkIds: [],
- checkNames: [],
- goodParams: {
- page_no: 1,
- page_size: 20,
- // fixedCondition: ' enabled = 1 ',
- sort: 'create_time',
- dir: 'desc'
- },
- configLoading: false,
- /** 列表参数 */
- params: {
- page_no: 1,
- page_size: 20,
- sort: 'create_time',
- dir: 'desc'
- }
- }
- },
- created() {
- this.loadOptions()
- },
- methods: {
- async loadOptions() {
- this.configLoading = true
- try {
- let res = await API_configOptions.getList(this.params)
- res.data.forEach(item => {
- if (item.product_names) {
- item.show_product_names = item.product_names.split('=')
- }
- if (item.product_ids) {
- item.show_product_ids = item.product_ids.split(',')
- }
- // 初始化商品展开状态
- item.productsExpanded = false
- })
- this.optionList = res
- } catch (error) {
- this.$message.error('加载配置项失败')
- } finally {
- this.configLoading = false
- }
- },
- /** 分页大小发生改变 */
- handlePageSizeChange(size) {
- this.params.page_size = size
- this.loadOptions()
- },
- /** 分页页数发生改变 */
- handlePageCurrentChange(page) {
- this.params.page_no = page
- this.loadOptions()
- },
- addOptionValues() {
- this.editMode = false
- this.optionForm = {
- id: null,
- name: '',
- type: 'radio',
- option_values: [{ value: '', material_code: '', status: 1 }]
- }
- this.showAddDialog = true
- },
- getTypeLabel(type) {
- const types = {
- radio: '单选',
- checkbox: '多选',
- text: '文本'
- }
- return types[type] || type
- },
- getTypeTag(type) {
- const types = {
- radio: 'primary',
- checkbox: 'success',
- text: 'info'
- }
- return types[type] || 'info'
- },
- searchOption() {
- this.params.page_no = 1
- this.loadOptions()
- },
- handleProductTagClick(name) {
- this.params.keyword = name
- this.searchOption()
- },
- async handleProductTagClose(row, index) {
- try {
- await this.$confirm('确定删除商品吗?')
- row.show_product_ids.splice(index, 1)
- row.show_product_names.splice(index, 1)
- const data = {
- id: row.id,
- product_ids: row.show_product_ids.join(','),
- product_names: row.show_product_names.join('=')
- }
- await API_configOptions.editModel(data)
- this.$message.success('删除成功')
- this.loadOptions() // 重新加载列表
- } catch (error) {
- if (error !== 'cancel') {
- this.$message.error('删除失败')
- }
- }
- },
- showProductSelector(option) {
- this.currentEditingOption = option
- if (option.show_product_ids) {
- this.checkIds = [...option.show_product_ids]
- this.checkNames = [...option.show_product_names]
- } else {
- this.checkIds = []
- this.checkNames = []
- }
- this.loadProducts()
- this.showProductSelectorDialog = true
- },
- async loadProducts() {
- this.loading = true
- try {
- this.filteredProducts = await API_goods.getGoodsList(this.goodParams)
- } catch (error) {
- this.$message.error('加载商品列表失败')
- } finally {
- this.loading = false
- }
- },
- handleSizeChange(val) {
- this.goodParams.page_size = val
- this.loadProducts()
- },
- handleCurrentChange(val) {
- this.goodParams.page_no = val
- this.loadProducts()
- },
- handleProductSearch() {
- this.goodParams = {
- ...this.goodParams,
- query: this.productSearchQuery
- }
- this.loadProducts()
- },
- async setProductAPI(option) {
- if (!this.goodId) {
- this.$message.error('请先选择商品')
- return
- }
- if (option.show_product_ids) {
- this.checkIds = [...option.show_product_ids]
- this.checkNames = [...option.show_product_names]
- } else {
- this.checkIds = []
- this.checkNames = []
- }
- if (this.checkIds.indexOf(this.goodId + '') !== -1) {
- this.$message.error('商品已存在')
- return
- }
- this.checkIds.push(this.goodId + '')
- this.checkNames.push(this.goodName)
- await API_configOptions.editModel({
- id: option.id,
- product_ids: this.checkIds.join(','),
- product_names: this.checkNames.join('=')
- })
- this.$message.success('保存成功')
- this.loadOptions() // 重新加载列表
- },
- handleProductSelectionChange(selection) {
- this.selectedProductIds = selection.map(item => item.id)
- this.selectedProductNames = selection.map(item => item.name)
- },
- async saveProductSelection() {
- if (this.currentEditingOption) {
- try {
- // 过滤掉已存在的商品ID,避免重复添加
- this.checkIds.map(id => this.selectedProductIds.push(Number(id)))
- this.checkNames.map(name => this.selectedProductNames.push(name))
- // 数组去重
- this.selectedProductIds = [...new Set(this.selectedProductIds)]
- this.selectedProductNames = [...new Set(this.selectedProductNames)]
- const productNames = this.selectedProductNames.join('=')
- const productIds = this.selectedProductIds.join(',')
- await API_configOptions.editModel({
- id: this.currentEditingOption.id,
- product_ids: productIds,
- product_names: productNames
- })
- this.currentEditingOption.productIds = [...this.selectedProductIds]
- this.$message.success('保存成功')
- this.loadOptions() // 重新加载列表
- } catch (error) {
- this.$message.error('保存失败')
- }
- }
- this.showProductSelectorDialog = false
- },
- editOption(row) {
- this.editMode = true
- this.optionForm = JSON.parse(JSON.stringify(row))
- this.showAddDialog = true
- },
- async deleteOption(row) {
- try {
- await this.$confirm('确定删除该配置项吗?')
- await API_configOptions.deletes(row.id)
- this.$message.success('删除成功')
- this.loadOptions() // 重新加载列表
- } catch (error) {
- if (error !== 'cancel') {
- this.$message.error('删除失败')
- }
- }
- },
- removeOption(item, index) {
- if (this.optionForm.option_values.length === 1 || this.optionForm.option_values.filter(v => v.status === 1).length === 1) {
- this.$message.warning('至少保留一个选项')
- return
- }
- this.$confirm('确定要删除这个选项吗?', '提示', {
- confirmButtonText: '确定',
- cancelButtonText: '取消',
- type: 'warning'
- }).then(() => {
- if (this.editMode && item.id) {
- this.optionForm.option_values[index].status = 0
- // API_configOptions.deletesOptionValues(item.id)
- } else {
- this.optionForm.option_values.splice(index, 1)
- }
- this.$message.success('删除成功')
- }).catch(() => {})
- },
- async saveOption() {
- if (!this.optionForm.name) {
- this.$message.warning('请输入配置项名称')
- return
- }
- if ((this.optionForm.type === 'radio' || this.optionForm.type === 'checkbox') && !this.optionForm.option_values) {
- this.$message.warning('请填写可选内容')
- return
- }
- // 新增校验:每个选项的 value 和 material_code 都必填
- if ((this.optionForm.type === 'radio' || this.optionForm.type === 'checkbox')) {
- const empty = this.optionForm.option_values.some(v => v.status && (!v.value || !v.material_code))
- if (empty) {
- this.$message.warning('请填写所有选项的内容和物料编号')
- return
- }
- }
- try {
- if (this.editMode) {
- await API_configOptions.update(this.optionForm)
- } else {
- // const data = {
- // name: this.optionForm.name,
- // type: this.optionForm.type,
- // option_values: this.optionForm.option_values
- // .filter(v => v.status)
- // .map(v => ({ value: v.value, material_code: v.material_code }))
- // }
- await API_configOptions.addModel(this.optionForm)
- }
- this.$message.success('保存成功')
- this.showAddDialog = false
- this.editMode = false
- this.optionForm = { id: null, name: '', type: 'radio', option_values: [{ value: '', material_code: '', status: 1 }], productIds: [] }
- this.loadOptions() // 重新加载列表
- } catch (error) {
- this.$message.error('保存失败')
- }
- },
- toggleProductsExpanded(item) {
- this.$set(item, 'productsExpanded', !item.productsExpanded)
- }
- }
- }
- </script>
- <style scoped>
- .config-container {
- padding: 20px;
- }
- .search-section {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 20px;
- }
- .search-inputs {
- display: flex;
- gap: 10px;
- flex: 1;
- margin-right: 20px;
- }
- .search-inputs .el-input {
- width: 300px;
- }
- .search-inputs .el-select {
- width: 200px;
- }
- .option-form {
- padding: 20px;
- }
- .option-values {
- display: flex;
- flex-direction: column;
- gap: 10px;
- }
- .option-item {
- display: flex;
- align-items: center;
- margin-bottom: 10px;
- }
- .product-selector {
- .search-section {
- margin-bottom: 20px;
- display: flex;
- align-items: center;
- }
- .product-list {
- margin-top: 20px;
- }
- }
- .el-tag {
- cursor: pointer;
- transition: all 0.3s;
- }
- .el-tag:hover {
- opacity: 0.8;
- }
- .product-actions {
- display: flex;
- align-items: center;
- }
- </style>
|