| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- <template>
- <div class="tag-edit">
- <el-card>
- <template #header>
- <div class="header">
- <span class="title">{{ isEdit ? '编辑标签' : '新建标签' }}</span>
- <span v-if="isEdit && currentTag" class="tag-id">ID: {{ currentTag.id }}</span>
- </div>
- </template>
- <!-- 基础信息 -->
- <div class="section">
- <div class="section-title">基础信息</div>
- <el-form
- ref="form"
- :model="form"
- :rules="rules"
- label-width="100px"
- style="max-width: 760px"
- >
- <el-form-item label="标签名称" prop="name">
- <el-input v-model="form.name" placeholder="最长 40 字符" maxlength="40" show-word-limit />
- </el-form-item>
- <el-form-item label="标签描述" prop="description">
- <el-input
- v-model="form.description"
- type="textarea"
- :rows="3"
- placeholder="便于识别标签用途,最长 200 字符"
- maxlength="200"
- show-word-limit
- />
- </el-form-item>
- <el-form-item label="状态">
- <el-switch v-model="form.status" active-text="启用" inactive-text="停用" />
- </el-form-item>
- </el-form>
- </div>
- <!-- 规则配置 -->
- <div class="section">
- <div class="section-title">规则配置</div>
- <rule-builder v-model="form.groups" />
- </div>
- <!-- 操作按钮 -->
- <div class="footer">
- <el-button @click="handleCancel">取消</el-button>
- <el-button type="primary" @click="handleSave">保存</el-button>
- </div>
- </el-card>
- </div>
- </template>
- <script>
- import { ElMessage, ElMessageBox } from 'element-plus'
- import tagStore from '@/store/tagStore'
- import RuleBuilder from '@/components/tag/RuleBuilder.vue'
- import { PARAM_MAP, needValueInput } from '@/config/tagParams'
- export default {
- name: 'TagEdit',
- components: { RuleBuilder },
- data() {
- return {
- form: {
- name: '',
- description: '',
- status: true,
- groups: []
- },
- rules: {
- name: [
- { required: true, message: '请输入标签名称', trigger: 'blur' },
- { max: 40, message: '最长 40 字符', trigger: 'blur' }
- ],
- description: [
- { max: 200, message: '最长 200 字符', trigger: 'blur' }
- ]
- },
- currentTag: null,
- initialSnapshot: ''
- }
- },
- computed: {
- isEdit() {
- return !!this.$route.params.id
- }
- },
- created() {
- if (this.isEdit) {
- const target = tagStore.getById(this.$route.params.id)
- if (!target) {
- ElMessage.error('标签不存在')
- this.$router.replace('/customization/hsbc/tags')
- return
- }
- this.currentTag = target
- this.form = {
- name: target.name,
- description: target.description,
- status: target.status === 'enabled',
- groups: JSON.parse(JSON.stringify(target.groups || []))
- }
- }
- this.initialSnapshot = JSON.stringify(this.form)
- },
- methods: {
- validateRules() {
- if (!this.form.groups.length) {
- ElMessage.error('至少配置 1 个规则组')
- return false
- }
- for (let i = 0; i < this.form.groups.length; i++) {
- const group = this.form.groups[i]
- if (!group.rules || !group.rules.length) {
- ElMessage.error(`规则组 ${i + 1} 至少配置 1 条规则`)
- return false
- }
- for (let j = 0; j < group.rules.length; j++) {
- const rule = group.rules[j]
- const label = `规则组 ${i + 1} 第 ${j + 1} 条规则`
- if (!rule.paramKey) {
- ElMessage.error(`${label}:请选择参数`)
- return false
- }
- if (!rule.operator) {
- ElMessage.error(`${label}:请选择运算符`)
- return false
- }
- if (needValueInput(rule.operator)) {
- const param = PARAM_MAP[rule.paramKey]
- // 相对日期:需要 N 值和单位
- if (['withinLast', 'beforeLast'].includes(rule.operator)) {
- if (rule.value === '' || rule.value === null || rule.value === undefined) {
- ElMessage.error(`${label}:请输入相对日期数值`)
- return false
- }
- } else if (Array.isArray(rule.value)) {
- if (!rule.value.length) {
- ElMessage.error(`${label}:请选择比较值`)
- return false
- }
- } else if (rule.value === '' || rule.value === null || rule.value === undefined) {
- ElMessage.error(`${label}:请输入比较值`)
- return false
- }
- if (rule.operator === 'between') {
- if (rule.value2 === '' || rule.value2 === null || rule.value2 === undefined) {
- ElMessage.error(`${label}:请输入区间上限`)
- return false
- }
- if (param && (param.type === 'BigDecimal' || param.type === 'List')
- && Number(rule.value) > Number(rule.value2)) {
- ElMessage.error(`${label}:区间下限不能大于上限`)
- return false
- }
- }
- }
- // 时间窗口校验
- if (param_hasHistory(rule.paramKey)) {
- const tw = rule.timeWindow || {}
- if (tw.mode === 'specificMonth' && !tw.month) {
- ElMessage.error(`${label}:请选择指定月份`)
- return false
- }
- if (tw.mode === 'recentMonths') {
- if (!tw.n || tw.n < 1 || tw.n > 12) {
- ElMessage.error(`${label}:近 N 个月 的 N 需在 1-12 之间`)
- return false
- }
- if (tw.aggregator === 'atLeast') {
- if (!tw.m || tw.m < 1 || tw.m > tw.n) {
- ElMessage.error(`${label}:至少 M 个月 的 M 不能大于 N`)
- return false
- }
- }
- }
- }
- }
- }
- return true
- },
- handleSave() {
- this.$refs.form.validate((valid) => {
- if (!valid) return
- if (!this.validateRules()) return
- const doSave = () => {
- if (this.isEdit) {
- tagStore.update(this.currentTag.id, this.form)
- ElMessage.success('保存成功')
- } else {
- tagStore.create(this.form)
- ElMessage.success('新建成功')
- }
- this.$router.push('/customization/hsbc/tags')
- }
- if (this.isEdit && this.currentTag && this.currentTag.referenceCount > 0) {
- ElMessageBox.confirm(
- `该标签已被 ${this.currentTag.referenceCount} 个权益/活动引用,修改后将影响其客群匹配,确认保存?`,
- '提示',
- { type: 'warning' }
- ).then(doSave).catch(() => {})
- } else {
- doSave()
- }
- })
- },
- handleCancel() {
- const dirty = JSON.stringify(this.form) !== this.initialSnapshot
- if (dirty) {
- ElMessageBox.confirm('未保存内容将丢失,确认退出?', '提示', { type: 'warning' })
- .then(() => this.$router.push('/customization/hsbc/tags'))
- .catch(() => {})
- } else {
- this.$router.push('/customization/hsbc/tags')
- }
- }
- }
- }
- function param_hasHistory(key) {
- const p = PARAM_MAP[key]
- return p && p.hasHistory
- }
- </script>
- <style scoped>
- .tag-edit { padding: 0; }
- .header {
- display: flex;
- align-items: center;
- gap: 16px;
- }
- .title { font-size: 16px; font-weight: 600; }
- .tag-id { color: #909399; font-size: 13px; }
- .section {
- padding: 16px 0 20px;
- border-bottom: 1px dashed #ebeef5;
- }
- .section:last-of-type { border-bottom: none; }
- .section-title {
- font-size: 15px;
- font-weight: 600;
- color: #303133;
- margin-bottom: 16px;
- padding-left: 8px;
- border-left: 3px solid #409EFF;
- }
- .footer {
- display: flex;
- justify-content: center;
- gap: 16px;
- padding-top: 20px;
- }
- </style>
|