| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650 |
- <template>
- <div class="limit-purchase-rule-container">
- <!-- 规则列表区域 -->
- <el-card class="rule-list-card">
- <template #header>
- <div class="card-header">
- <span>限购规则列表</span>
- <el-button type="primary" size="small" @click="handleCreateRule">
- 新建规则
- </el-button>
- </div>
- </template>
- <el-table
- v-loading="loading.list"
- :data="ruleList"
- border
- style="width: 100%"
- >
- <el-table-column prop="id" label="ID" width="80" />
- <el-table-column prop="name" label="规则名称" min-width="160" />
- <el-table-column label="限购地区" min-width="260">
- <template #default="scope">
- <div v-if="scope.row.regions && scope.row.regions.length">
- <el-tag
- v-for="(region, index) in scope.row.regions"
- :key="index"
- type="danger"
- size="small"
- class="region-tag"
- >
- {{ formatRegionLabel(region) }}
- </el-tag>
- </div>
- <span v-else class="text-muted">未设置</span>
- </template>
- </el-table-column>
- <el-table-column prop="is_default" label="默认规则" width="100">
- <template #default="scope">
- <el-tag :type="scope.row.is_default ? 'success' : 'info'" size="small">
- {{ scope.row.is_default ? '是' : '否' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column prop="status" label="状态" width="100">
- <template #default="scope">
- <el-tag :type="scope.row.status ? 'success' : 'info'" size="small">
- {{ scope.row.status ? '启用' : '停用' }}
- </el-tag>
- </template>
- </el-table-column>
- <el-table-column label="操作" width="200" fixed="right">
- <template #default="scope">
- <el-button type="primary" link size="small" @click="handleEditRule(scope.row)">
- 编辑
- </el-button>
- <el-button type="danger" link size="small" @click="handleDeleteRule(scope.row)">
- 删除
- </el-button>
- </template>
- </el-table-column>
- </el-table>
-
- <!-- 分页组件 -->
- <el-pagination
- v-if="pagination.total > 0"
- layout="prev, pager, next"
- :current-page="pagination.page"
- :page-size="pagination.pageSize"
- :total="pagination.total"
- @current-change="handlePageChange"
- style="margin-top: 20px; text-align: right;"
- />
- </el-card>
- <!-- 规则表单弹窗:新建 / 编辑规则基本信息 -->
- <el-dialog
- :title="ruleForm.id ? '编辑限购规则' : '新建限购规则'"
- v-model="ruleDialog.visible"
- width="720px"
- :close-on-click-modal="false"
- >
- <el-form
- ref="ruleFormRef"
- :model="ruleForm"
- :rules="ruleRules"
- label-width="90px"
- >
- <el-form-item label="规则名称" prop="name">
- <el-input v-model="ruleForm.name" maxlength="64" show-word-limit />
- </el-form-item>
- <el-form-item label="是否默认" prop="is_default">
- <el-switch
- v-model="ruleForm.is_default"
- :active-value="true"
- :inactive-value="false"
- />
- <div class="form-tip">设置默认后未设置限购规则的商品都使用该规则</div>
- </el-form-item>
- <el-form-item label="状态" prop="status">
- <el-switch
- v-model="ruleForm.status"
- :active-value="true"
- :inactive-value="false"
- active-text="启用"
- inactive-text="停用"
- />
- </el-form-item>
- <!-- 在新建 / 编辑规则里直接设置限购地区(本地省市区 JSON 级联) -->
- <el-form-item label="限购地区">
- <div class="region-selector-wrapper">
- <el-form label-width="60px" class="region-form">
- <el-row :gutter="20" align="middle">
- <el-col :span="8">
- <el-form-item label="省份">
- <el-select
- v-model="selectedProvince"
- placeholder="请选择省份"
- filterable
- clearable
- @change="onProvinceChange"
- >
- <el-option
- v-for="p in provinces"
- :key="p.code"
- :label="p.name"
- :value="p.name"
- />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="城市">
- <el-select
- v-model="selectedCity"
- placeholder="请选择城市"
- :disabled="!selectedProvince"
- filterable
- clearable
- @change="onCityChange"
- >
- <el-option
- v-for="c in cities"
- :key="c.code"
- :label="c.name"
- :value="c.name"
- />
- </el-select>
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="区县">
- <el-select
- v-model="selectedArea"
- placeholder="请选择区县"
- :disabled="!selectedCity"
- filterable
- clearable
- >
- <el-option
- v-for="a in areas"
- :key="a.code"
- :label="a.name"
- :value="a.name"
- />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
- <el-row :gutter="20">
- <el-col :span="24" class="text-right" style="margin-top: 10px;">
- <el-button type="primary" @click="addRegion">
- 添加限购地区
- </el-button>
- </el-col>
- </el-row>
- </el-form>
- <div class="region-tip">注:可只选择到省或市层级</div>
- <div class="limited-region-list">
- <div class="list-title">已设置的限购地区</div>
- <div v-if="currentRuleRegions.length === 0" class="empty-tip">
- 暂未添加限购地区
- </div>
- <div
- v-for="(area, index) in currentRuleRegions"
- :key="index"
- class="limited-area-item"
- >
- <span>{{ formatRegionLabel(area) }}</span>
- <el-button
- type="danger"
- link
- @click="removeRegion(index)"
- >
- 删除
- </el-button>
- </div>
- </div>
- </div>
- </el-form-item>
- </el-form>
- <template #footer>
- <span class="dialog-footer">
- <el-button @click="ruleDialog.visible = false">取 消</el-button>
- <el-button type="primary" :loading="loading.saveRule" @click="submitRuleForm">
- 确 认
- </el-button>
- </span>
- </template>
- </el-dialog>
- </div>
- </template>
- <script>
- export default {
- name: 'LimitPurchaseRule',
- props: {
- axiosInstance: {
- type: Object,
- default: null
- }
- },
- data() {
- return {
- // 列表数据
- ruleList: [],
- loading: {
- list: false,
- saveRule: false
- },
-
- // 分页数据
- pagination: {
- page: 1,
- pageSize: 15,
- total: 0,
- hasMore: false
- },
- // 规则表单
- ruleDialog: {
- visible: false
- },
- ruleForm: {
- id: null,
- name: '',
- is_default: 0,
- status: 1
- },
- ruleRules: {
- name: [
- { required: true, message: '请输入规则名称', trigger: 'blur' }
- ]
- },
- // 使用接口获取省市区数据
- provinces: [],
- cities: [],
- areas: [],
- selectedProvince: '',
- selectedCity: '',
- selectedArea: '',
- currentRuleRegions: []
- }
- },
- created() {
- this.fetchRuleList()
- this.fetchProvinces()
- },
- methods: {
- http() {
- // 默认用全局 axios,如果父组件传入则优先用父组件的
- return this.axiosInstance || this.$axios || this.$http
- },
- // 获取省份数据
- async fetchProvinces() {
- if (!this.http()) return
- try {
- const response = await this.http().get('/limit_purchase/region/province')
- this.provinces = response.data || []
- } catch (e) {
- console.error('获取省份数据失败:', e)
- this.$message.error('获取省份数据失败')
- }
- },
- // ========== 列表相关 ==========
- async fetchRuleList() {
- if (!this.http()) return
- this.loading.list = true
- try {
- const { page } = await this.http().get('/limit_purchase/rule', {
- params: {
- page: this.pagination.page,
- limit: this.pagination.pageSize
- }
- })
-
- // 根据分页数据结构处理
- if (page) {
- this.ruleList = Array.isArray(page.data) ? page.data : []
- this.pagination.total = page.total || 0
- this.pagination.hasMore = page.has_more || false
- } else {
- // 兼容旧的数据结构
- this.ruleList = []
- this.pagination.total = 0
- this.pagination.hasMore = false
- }
- } catch (e) {
- console.error(e)
- this.$message.error('加载规则列表失败')
- } finally {
- this.loading.list = false
- }
- },
-
- handlePageChange(page) {
- this.pagination.page = page
- this.fetchRuleList()
- },
- handleCreateRule() {
- this.ruleForm = {
- id: null,
- name: '',
- is_default: false,
- status: true
- }
- // 新建规则时清空当前规则地区
- this.currentRuleRegions = []
- this.selectedProvince = ''
- this.selectedCity = ''
- this.selectedArea = ''
- this.cities = []
- this.areas = []
- this.ruleDialog.visible = true
- this.$nextTick(() => {
- if (this.$refs.ruleFormRef && typeof this.$refs.ruleFormRef.clearValidate === 'function') {
- this.$refs.ruleFormRef.clearValidate()
- }
- })
- },
- handleEditRule(row) {
- this.ruleForm = {
- id: row.id,
- name: row.name,
- is_default: row.is_default || false,
- status: row.status
- }
- // 编辑规则时加载该规则已有的地区
- this.currentRuleRegions = Array.isArray(row.regions) ? [...row.regions] : []
- this.selectedProvince = ''
- this.selectedCity = ''
- this.selectedArea = ''
- this.cities = []
- this.areas = []
- this.ruleDialog.visible = true
- this.$nextTick(() => {
- if (this.$refs.ruleFormRef && typeof this.$refs.ruleFormRef.clearValidate === 'function') {
- this.$refs.ruleFormRef.clearValidate()
- }
- })
- },
- submitRuleForm() {
- this.$refs.ruleFormRef.validate(async (valid) => {
- if (!valid || !this.http()) return
- this.loading.saveRule = true
- try {
- if (this.ruleForm.id) {
- // 更新:名称 + 是否默认 + 状态 + 限购地区
- await this.http().put(`/limit_purchase/rule/${this.ruleForm.id}`, {
- name: this.ruleForm.name,
- is_default: this.ruleForm.is_default,
- status: this.ruleForm.status,
- regions: this.currentRuleRegions
- })
- this.$message.success('更新成功')
- } else {
- // 创建:名称 + 是否默认 + 状态 + 限购地区
- await this.http().post('/limit_purchase/rule', {
- name: this.ruleForm.name,
- is_default: this.ruleForm.is_default,
- status: this.ruleForm.status,
- regions: this.currentRuleRegions
- })
- this.$message.success('创建成功')
- }
- this.ruleDialog.visible = false
- this.fetchRuleList()
- } catch (e) {
- console.error(e)
- this.$message.error('保存失败')
- } finally {
- this.loading.saveRule = false
- }
- })
- },
- handleDeleteRule(row) {
- if (!this.http()) return
- this.$confirm(`确认删除规则「${row.name}」吗?`, '提示', {
- type: 'warning'
- })
- .then(async () => {
- try {
- const {code,msg} = await this.http().delete(`/limit_purchase/rule/${row.id}`)
- if (code === 0 || code === 200) {
- this.$message.success('删除成功')
- this.fetchRuleList()
- } else {
- this.$message.error(msg || '删除失败')
- }
- } catch (e) {
- console.error(e)
- this.$message.error('删除失败')
- }
- })
- .catch(() => {})
- },
- // 从网络接口获取城市数据
- async onProvinceChange() {
- this.selectedCity = ''
- this.selectedArea = ''
- this.areas = []
- this.cities = []
-
- if (!this.selectedProvince) return
-
- try {
- const province = this.provinces.find(p => p.name === this.selectedProvince)
- if (province) {
- // 获取城市数据
- const response = await this.http().get(`/limit_purchase/region/city/${province.code}`)
- this.cities = response.data || []
- }
- } catch (e) {
- console.error('获取城市数据失败:', e)
- this.$message.error('获取城市数据失败')
- }
- },
- // 从网络接口获取区县数据
- async onCityChange() {
- this.selectedArea = ''
- this.areas = []
-
- if (!this.selectedCity || !this.selectedProvince) return
-
- try {
- const province = this.provinces.find(p => p.name === this.selectedProvince)
- if (province) {
- const city = this.cities.find(c => c.name === this.selectedCity)
- if (city) {
- const response = await this.http().get(`/limit_purchase/region/area/${city.code}`)
- this.areas = response.data || []
- }
- }
- } catch (e) {
- console.error('获取区县数据失败:', e)
- this.$message.error('获取区县数据失败')
- }
- },
- addRegion() {
- if (!this.selectedProvince) {
- this.$message.warning('请至少选择省份')
- return
- }
- // 查找选中的省市区对象
- const provinceObj = this.provinces.find(p => p.name === this.selectedProvince)
- const cityObj = this.cities.find(c => c.name === this.selectedCity) || null
- const areaObj = this.areas.find(a => a.name === this.selectedArea) || null
- let region = {
- province_id: provinceObj ? provinceObj.code : null,
- province_name: this.selectedProvince,
- city_id: cityObj ? cityObj.code : null,
- city_name: this.selectedCity || null,
- area_id: areaObj ? areaObj.code : null,
- area_name: this.selectedArea || null
- };
- // 去重:检查是否已经存在相同或更广范围的地区
- const exists = this.currentRuleRegions.some(item => {
- // 完全匹配检查
- if (item.province_name !== region.province_name) return false;
-
- // 如果当前添加的是省级规则
- if (!region.city_name) {
- return !item.city_name; // 匹配已有省级规则
- }
-
- // 如果当前添加的是市级规则
- if (region.city_name && !region.area_name) {
- return item.city_name === region.city_name && !item.area_name;
- }
-
- // 如果当前添加的是区级规则
- if (region.city_name && region.area_name) {
- return item.city_name === region.city_name && item.area_name === region.area_name;
- }
-
- return false;
- });
- if (exists) {
- this.$message.warning('该地区已在列表中')
- return
- }
- this.currentRuleRegions.push(region)
-
- // 清空已选项
- this.selectedProvince = ''
- this.selectedCity = ''
- this.selectedArea = ''
- this.cities = []
- this.areas = []
- },
- removeRegion(index) {
- this.currentRuleRegions.splice(index, 1)
- },
- // ========== 工具方法 ==========
- formatRegionLabel(region) {
- const parts = []
- if (region.province_name) parts.push(region.province_name)
- if (region.city_name) parts.push(region.city_name)
- if (region.area_name) parts.push(region.area_name)
- return parts.join(' - ') || '未知地区'
- }
- }
- }
- </script>
- <style scoped>
- .limit-purchase-rule-container {
- padding: 0;
- }
- .rule-list-card {
- margin-bottom: 0;
- }
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- }
- .region-tag {
- margin-right: 4px;
- margin-bottom: 4px;
- }
- .text-muted {
- color: #999;
- }
- .region-selector-wrapper {
- padding: 10px 0 0;
- }
- .region-selector-title {
- display: flex;
- align-items: center;
- margin-bottom: 16px;
- font-size: 14px;
- }
- .region-selector-title .text-danger {
- color: #f56c6c;
- }
- .region-form {
- margin-bottom: 16px;
- }
- .limited-region-list {
- background: #fff7f7;
- border-radius: 4px;
- padding: 12px 16px;
- }
- .inner-region-form {
- margin-bottom: 8px;
- }
- .limited-region-list .list-title {
- font-size: 14px;
- margin-bottom: 8px;
- }
- .limited-region-list .empty-tip {
- font-size: 13px;
- color: #999;
- }
- .limited-area-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 8px 0;
- border-bottom: 1px solid #f5d1d1;
- }
- .limited-area-item:last-child {
- border-bottom: none;
- }
- .dialog-footer {
- text-align: right;
- }
- .text-right {
- text-align: right;
- }
- .form-tip {
- margin-left: 10px;
- font-size: 12px;
- color: #999;
- }
- .region-tip {
- font-size: 12px;
- color: #999;
- margin-top: 5px;
- margin-bottom: 15px;
- }
- </style>
|