|
@@ -0,0 +1,699 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="limit-purchase-rule-container">
|
|
|
|
|
+ <!-- 规则列表区域 -->
|
|
|
|
|
+ <el-card class="rule-list-card">
|
|
|
|
|
+ <div slot="header" class="card-header">
|
|
|
|
|
+ <span>限购规则列表</span>
|
|
|
|
|
+ <el-button type="primary" size="small" @click="handleCreateRule">
|
|
|
|
|
+ 新建规则
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <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 slot-scope="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="mini"
|
|
|
|
|
+ class="region-tag"
|
|
|
|
|
+ >
|
|
|
|
|
+ {{ formatRegionLabel(region) }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <span v-else class="text-muted">未设置</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column prop="status" label="状态" width="100">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <el-tag :type="scope.row.status === 1 ? 'success' : 'info'" size="small">
|
|
|
|
|
+ {{ scope.row.status === 1 ? '启用' : '停用' }}
|
|
|
|
|
+ </el-tag>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="操作" width="200" fixed="right">
|
|
|
|
|
+ <template slot-scope="scope">
|
|
|
|
|
+ <el-button type="text" size="small" @click="handleEditRule(scope.row)">
|
|
|
|
|
+ 编辑
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button type="text" size="small" @click="handleEditRegions(scope.row)">
|
|
|
|
|
+ 设置限购地区
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ <el-button type="text" size="small" class="danger-text" @click="handleDeleteRule(scope.row)">
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 规则表单弹窗:新建 / 编辑规则基本信息 -->
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ :title="ruleForm.id ? '编辑限购规则' : '新建限购规则'"
|
|
|
|
|
+ :visible.sync="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="status">
|
|
|
|
|
+ <el-switch
|
|
|
|
|
+ v-model="ruleForm.status"
|
|
|
|
|
+ :active-value="1"
|
|
|
|
|
+ :inactive-value="0"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 在新建 / 编辑规则里直接设置限购地区(本地省市区 JSON 级联) -->
|
|
|
|
|
+ <el-form-item label="限购地区">
|
|
|
|
|
+ <div class="region-selector-wrapper">
|
|
|
|
|
+ <el-form label-width="60px" class="region-form inner-region-form">
|
|
|
|
|
+ <el-row :gutter="20" type="flex" align="middle">
|
|
|
|
|
+ <el-col :span="6">
|
|
|
|
|
+ <el-form-item label="省份">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="selectedProvince"
|
|
|
|
|
+ placeholder="请选择省份"
|
|
|
|
|
+ filterable
|
|
|
|
|
+ clearable
|
|
|
|
|
+ @change="onProvinceChange"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="p in provinces"
|
|
|
|
|
+ :key="p.name"
|
|
|
|
|
+ :label="p.name"
|
|
|
|
|
+ :value="p.name"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="6">
|
|
|
|
|
+ <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.name"
|
|
|
|
|
+ :label="c.name"
|
|
|
|
|
+ :value="c.name"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="6">
|
|
|
|
|
+ <el-form-item label="区县">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="selectedArea"
|
|
|
|
|
+ placeholder="请选择区县"
|
|
|
|
|
+ :disabled="!selectedCity"
|
|
|
|
|
+ filterable
|
|
|
|
|
+ clearable
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="a in areas"
|
|
|
|
|
+ :key="a"
|
|
|
|
|
+ :label="a"
|
|
|
|
|
+ :value="a"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="6" class="text-right">
|
|
|
|
|
+ <el-button type="primary" @click="addRegion">
|
|
|
|
|
+ <i class="el-icon-plus" /> 添加限购地区
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <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="text"
|
|
|
|
|
+ class="danger-text"
|
|
|
|
|
+ icon="el-icon-delete"
|
|
|
|
|
+ @click="removeRegion(index)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
|
|
+ <el-button @click="ruleDialog.visible = false">取 消</el-button>
|
|
|
|
|
+ <el-button type="primary" :loading="loading.saveRule" @click="submitRuleForm">
|
|
|
|
|
+ 确 认
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 地区设置弹窗:管理单条规则下的多个限购地区 -->
|
|
|
|
|
+ <el-dialog
|
|
|
|
|
+ title="设置限购地区"
|
|
|
|
|
+ :visible.sync="regionDialog.visible"
|
|
|
|
|
+ width="720px"
|
|
|
|
|
+ :close-on-click-modal="false"
|
|
|
|
|
+ >
|
|
|
|
|
+ <div class="region-selector-wrapper">
|
|
|
|
|
+ <div class="region-selector-title">
|
|
|
|
|
+ <i class="el-icon-warning-outline text-danger" />
|
|
|
|
|
+ <span> 设置限购地区</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <el-form label-width="60px" class="region-form">
|
|
|
|
|
+ <el-row :gutter="20" type="flex" align="middle">
|
|
|
|
|
+ <el-col :span="6">
|
|
|
|
|
+ <el-form-item label="省份">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="selectedProvince"
|
|
|
|
|
+ placeholder="请选择省份"
|
|
|
|
|
+ filterable
|
|
|
|
|
+ clearable
|
|
|
|
|
+ @change="onProvinceChange"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="p in provinces"
|
|
|
|
|
+ :key="p.name"
|
|
|
|
|
+ :label="p.name"
|
|
|
|
|
+ :value="p.name"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="6">
|
|
|
|
|
+ <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.name"
|
|
|
|
|
+ :label="c.name"
|
|
|
|
|
+ :value="c.name"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="6">
|
|
|
|
|
+ <el-form-item label="区县">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="selectedArea"
|
|
|
|
|
+ placeholder="请选择区县"
|
|
|
|
|
+ :disabled="!selectedCity"
|
|
|
|
|
+ filterable
|
|
|
|
|
+ clearable
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="a in areas"
|
|
|
|
|
+ :key="a"
|
|
|
|
|
+ :label="a"
|
|
|
|
|
+ :value="a"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="6" class="text-right">
|
|
|
|
|
+ <el-button type="primary" @click="addRegion">
|
|
|
|
|
+ <i class="el-icon-plus" /> 添加限购地区
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+
|
|
|
|
|
+ <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="text"
|
|
|
|
|
+ class="danger-text"
|
|
|
|
|
+ icon="el-icon-delete"
|
|
|
|
|
+ @click="removeRegion(index)"
|
|
|
|
|
+ >
|
|
|
|
|
+ 删除
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
|
|
+ <el-button @click="regionDialog.visible = false">取 消</el-button>
|
|
|
|
|
+ <el-button type="primary" :loading="loading.saveRegions" @click="submitRegions">
|
|
|
|
|
+ 确 认 设置
|
|
|
|
|
+ </el-button>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+// 前端内置省市区 JSON,不走接口
|
|
|
|
|
+// 这里只演示北京和天津两组数据,后续可按同样格式扩展
|
|
|
|
|
+const REGION_TREE = [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '北京市',
|
|
|
|
|
+ city: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '北京市',
|
|
|
|
|
+ area: [
|
|
|
|
|
+ '东城区',
|
|
|
|
|
+ '西城区',
|
|
|
|
|
+ '崇文区',
|
|
|
|
|
+ '宣武区',
|
|
|
|
|
+ '朝阳区',
|
|
|
|
|
+ '丰台区',
|
|
|
|
|
+ '石景山区',
|
|
|
|
|
+ '海淀区',
|
|
|
|
|
+ '门头沟区',
|
|
|
|
|
+ '房山区',
|
|
|
|
|
+ '通州区',
|
|
|
|
|
+ '顺义区',
|
|
|
|
|
+ '昌平区',
|
|
|
|
|
+ '大兴区',
|
|
|
|
|
+ '平谷区',
|
|
|
|
|
+ '怀柔区',
|
|
|
|
|
+ '密云县',
|
|
|
|
|
+ '延庆县'
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '天津市',
|
|
|
|
|
+ city: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '天津市',
|
|
|
|
|
+ area: [
|
|
|
|
|
+ '和平区',
|
|
|
|
|
+ '河东区',
|
|
|
|
|
+ '河西区',
|
|
|
|
|
+ '南开区',
|
|
|
|
|
+ '河北区',
|
|
|
|
|
+ '红桥区',
|
|
|
|
|
+ '塘沽区',
|
|
|
|
|
+ '汉沽区',
|
|
|
|
|
+ '大港区',
|
|
|
|
|
+ '东丽区',
|
|
|
|
|
+ '西青区',
|
|
|
|
|
+ '津南区',
|
|
|
|
|
+ '北辰区',
|
|
|
|
|
+ '武清区',
|
|
|
|
|
+ '宝坻区',
|
|
|
|
|
+ '宁河县',
|
|
|
|
|
+ '静海县',
|
|
|
|
|
+ '蓟县'
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+export default {
|
|
|
|
|
+ name: 'LimitPurchaseRule',
|
|
|
|
|
+ props: {
|
|
|
|
|
+ axiosInstance: {
|
|
|
|
|
+ type: Object,
|
|
|
|
|
+ default: null
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ // 列表数据
|
|
|
|
|
+ ruleList: [],
|
|
|
|
|
+ loading: {
|
|
|
|
|
+ list: false,
|
|
|
|
|
+ saveRule: false,
|
|
|
|
|
+ saveRegions: false
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 规则表单
|
|
|
|
|
+ ruleDialog: {
|
|
|
|
|
+ visible: false
|
|
|
|
|
+ },
|
|
|
|
|
+ ruleForm: {
|
|
|
|
|
+ id: null,
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ status: 1
|
|
|
|
|
+ },
|
|
|
|
|
+ ruleRules: {
|
|
|
|
|
+ name: [
|
|
|
|
|
+ { required: true, message: '请输入规则名称', trigger: 'blur' }
|
|
|
|
|
+ ]
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // 地区相关
|
|
|
|
|
+ regionDialog: {
|
|
|
|
|
+ visible: false,
|
|
|
|
|
+ ruleId: null
|
|
|
|
|
+ },
|
|
|
|
|
+ // 使用本地省市区 JSON 级联
|
|
|
|
|
+ provinces: REGION_TREE,
|
|
|
|
|
+ cities: [],
|
|
|
|
|
+ areas: [],
|
|
|
|
|
+ selectedProvince: '',
|
|
|
|
|
+ selectedCity: '',
|
|
|
|
|
+ selectedArea: '',
|
|
|
|
|
+ currentRuleRegions: [] // 当前规则下的地区数组
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ created() {
|
|
|
|
|
+ this.fetchRuleList()
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ http() {
|
|
|
|
|
+ // 默认用全局 axios,如果父组件传入则优先用父组件的
|
|
|
|
|
+ return this.axiosInstance || this.$axios || this.$http
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 列表相关 ==========
|
|
|
|
|
+ async fetchRuleList() {
|
|
|
|
|
+ if (!this.http()) return
|
|
|
|
|
+ this.loading.list = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ const { data } = await this.http().get('/limit_purchase/rule')
|
|
|
|
|
+ // 按项目通用返回结构处理
|
|
|
|
|
+ this.ruleList = Array.isArray(data?.data) ? data.data : (data || [])
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e)
|
|
|
|
|
+ this.$message.error('加载规则列表失败')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.loading.list = false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleCreateRule() {
|
|
|
|
|
+ this.ruleForm = {
|
|
|
|
|
+ id: null,
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ status: 1
|
|
|
|
|
+ }
|
|
|
|
|
+ // 新建规则时清空当前规则地区
|
|
|
|
|
+ this.currentRuleRegions = []
|
|
|
|
|
+ this.selectedProvince = ''
|
|
|
|
|
+ this.selectedCity = ''
|
|
|
|
|
+ this.selectedArea = ''
|
|
|
|
|
+ this.cities = []
|
|
|
|
|
+ this.areas = []
|
|
|
|
|
+
|
|
|
|
|
+ this.ruleDialog.visible = true
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.$refs.ruleFormRef && this.$refs.ruleFormRef.clearValidate()
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleEditRule(row) {
|
|
|
|
|
+ this.ruleForm = {
|
|
|
|
|
+ id: row.id,
|
|
|
|
|
+ name: row.name,
|
|
|
|
|
+ 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(() => {
|
|
|
|
|
+ this.$refs.ruleFormRef && 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,
|
|
|
|
|
+ status: this.ruleForm.status,
|
|
|
|
|
+ regions: this.currentRuleRegions
|
|
|
|
|
+ })
|
|
|
|
|
+ this.$message.success('更新成功')
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 创建:名称 + 状态 + 限购地区
|
|
|
|
|
+ await this.http().post('/limit_purchase/rule', {
|
|
|
|
|
+ name: this.ruleForm.name,
|
|
|
|
|
+ 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 {
|
|
|
|
|
+ await this.http().delete(`/limit_purchase/rule/${row.id}`)
|
|
|
|
|
+ this.$message.success('删除成功')
|
|
|
|
|
+ this.fetchRuleList()
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e)
|
|
|
|
|
+ this.$message.error('删除失败')
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(() => {})
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 地区设置相关 ==========
|
|
|
|
|
+
|
|
|
|
|
+ handleEditRegions(row) {
|
|
|
|
|
+ if (!row || !row.id) return
|
|
|
|
|
+ this.regionDialog.ruleId = row.id
|
|
|
|
|
+ this.selectedProvince = ''
|
|
|
|
|
+ this.selectedCity = ''
|
|
|
|
|
+ this.selectedArea = ''
|
|
|
|
|
+ this.cities = []
|
|
|
|
|
+ this.areas = []
|
|
|
|
|
+ this.currentRuleRegions = Array.isArray(row.regions) ? [...row.regions] : []
|
|
|
|
|
+ this.regionDialog.visible = true
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ onProvinceChange() {
|
|
|
|
|
+ this.selectedCity = ''
|
|
|
|
|
+ this.selectedArea = ''
|
|
|
|
|
+ this.areas = []
|
|
|
|
|
+ const province = REGION_TREE.find(p => p.name === this.selectedProvince)
|
|
|
|
|
+ this.cities = province ? province.city : []
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ onCityChange() {
|
|
|
|
|
+ this.selectedArea = ''
|
|
|
|
|
+ const province = REGION_TREE.find(p => p.name === this.selectedProvince)
|
|
|
|
|
+ const city = province ? province.city.find(c => c.name === this.selectedCity) : null
|
|
|
|
|
+ this.areas = city ? city.area : []
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ addRegion() {
|
|
|
|
|
+ if (!this.selectedProvince || !this.selectedCity || !this.selectedArea) {
|
|
|
|
|
+ this.$message.warning('请选择省、市、区县')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const region = {
|
|
|
|
|
+ province_id: null,
|
|
|
|
|
+ province_name: this.selectedProvince,
|
|
|
|
|
+ city_id: null,
|
|
|
|
|
+ city_name: this.selectedCity,
|
|
|
|
|
+ district_id: null,
|
|
|
|
|
+ district_name: this.selectedArea
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 去重:相同省市区不重复添加
|
|
|
|
|
+ const exists = this.currentRuleRegions.some(item =>
|
|
|
|
|
+ item.province_id === region.province_id &&
|
|
|
|
|
+ item.city_id === region.city_id &&
|
|
|
|
|
+ item.district_id === region.district_id
|
|
|
|
|
+ )
|
|
|
|
|
+ if (exists) {
|
|
|
|
|
+ this.$message.warning('该地区已在列表中')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ this.currentRuleRegions.push(region)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ removeRegion(index) {
|
|
|
|
|
+ this.currentRuleRegions.splice(index, 1)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async submitRegions() {
|
|
|
|
|
+ if (!this.http() || !this.regionDialog.ruleId) return
|
|
|
|
|
+ this.loading.saveRegions = true
|
|
|
|
|
+ try {
|
|
|
|
|
+ await this.http().post(`/limit_purchase/rule/${this.regionDialog.ruleId}/regions`, {
|
|
|
|
|
+ regions: this.currentRuleRegions
|
|
|
|
|
+ })
|
|
|
|
|
+ this.$message.success('限购地区设置成功')
|
|
|
|
|
+ this.regionDialog.visible = false
|
|
|
|
|
+ this.fetchRuleList()
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error(e)
|
|
|
|
|
+ this.$message.error('保存限购地区失败')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ this.loading.saveRegions = false
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // ========== 工具方法 ==========
|
|
|
|
|
+ formatRegionLabel(region) {
|
|
|
|
|
+ const parts = []
|
|
|
|
|
+ if (region.province_name) parts.push(region.province_name)
|
|
|
|
|
+ if (region.city_name) parts.push(region.city_name)
|
|
|
|
|
+ if (region.district_name) parts.push(region.district_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;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.danger-text {
|
|
|
|
|
+ color: #f56c6c;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.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;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|