feat(emoji): manage grouped access rules

This commit is contained in:
zhx 2026-07-23 19:21:40 +08:00
parent 942e678f1a
commit 3f7bf0d7f0
6 changed files with 159 additions and 16 deletions

View File

@ -5276,7 +5276,11 @@ export default {
}, },
table: { table: {
salePrice: 'Sale Price', salePrice: 'Sale Price',
shelfStatus: 'On/Off Shelf' shelfStatus: 'On/Off Shelf',
vipRange: 'VIP Range',
wealthRange: 'Wealth Range',
charmRange: 'Charm Range',
manageResources: 'Emojis'
}, },
form: { form: {
groupCode: 'Group Code', groupCode: 'Group Code',
@ -5284,7 +5288,12 @@ export default {
amount: 'Sale Price', amount: 'Sale Price',
shelfStatus: 'On/Off Shelf', shelfStatus: 'On/Off Shelf',
ktvStatus: 'KTV Emoji', ktvStatus: 'KTV Emoji',
weight: 'Weight' weight: 'Weight',
accessRules: 'Access Rules',
vipRange: 'VIP Level Range',
wealthRange: 'Wealth Level Range',
charmRange: 'Charm Level Range',
rangeHelp: '0 means that boundary is unrestricted. Users must meet every configured level rule.'
}, },
placeholder: { placeholder: {
groupCode: 'Group Code', groupCode: 'Group Code',
@ -5321,14 +5330,16 @@ export default {
offShelf: 'Off Shelf', offShelf: 'Off Shelf',
yes: 'Yes', yes: 'Yes',
no: 'No', no: 'No',
free: 'Free' free: 'Free',
unrestricted: 'Unrestricted'
}, },
dialog: { dialog: {
add: 'Add', add: 'Add',
edit: 'Edit' edit: 'Edit'
}, },
validation: { validation: {
required: 'This field is required' required: 'This field is required',
invalidLevelRange: 'Invalid level range: max must be 0 or greater than or equal to min'
}, },
message: { message: {
onShelfSuccess: 'Listed successfully', onShelfSuccess: 'Listed successfully',

View File

@ -5273,7 +5273,11 @@ export default {
}, },
table: { table: {
salePrice: '售卖价格', salePrice: '售卖价格',
shelfStatus: '上/下架' shelfStatus: '上/下架',
vipRange: 'VIP区间',
wealthRange: '财富区间',
charmRange: '魅力区间',
manageResources: '表情资源'
}, },
form: { form: {
groupCode: '分组Code', groupCode: '分组Code',
@ -5281,7 +5285,12 @@ export default {
amount: '售卖价格', amount: '售卖价格',
shelfStatus: '上/下架', shelfStatus: '上/下架',
ktvStatus: 'KTV表情', ktvStatus: 'KTV表情',
weight: '权重' weight: '权重',
accessRules: '使用权限',
vipRange: 'VIP等级区间',
wealthRange: '财富等级区间',
charmRange: '魅力等级区间',
rangeHelp: '0表示该边界不限制配置多类等级时用户需要同时满足全部条件。'
}, },
placeholder: { placeholder: {
groupCode: '分组Code', groupCode: '分组Code',
@ -5318,14 +5327,16 @@ export default {
offShelf: '下架', offShelf: '下架',
yes: '是', yes: '是',
no: '否', no: '否',
free: '免费' free: '免费',
unrestricted: '不限'
}, },
dialog: { dialog: {
add: '添加', add: '添加',
edit: '修改' edit: '修改'
}, },
validation: { validation: {
required: '必填字段不可为空' required: '必填字段不可为空',
invalidLevelRange: '等级区间无效最大值必须为0或不小于最小值'
}, },
message: { message: {
onShelfSuccess: '上架成功', onShelfSuccess: '上架成功',

View File

@ -317,6 +317,12 @@ function getFormData() {
export default { export default {
name: 'EmojiConfig', name: 'EmojiConfig',
components: { Pagination }, components: { Pagination },
props: {
initialGroup: {
type: Object,
default: null
}
},
data() { data() {
const commonRules = [ const commonRules = [
{ required: true, message: this.$t('pages.emojiManagement.validation.required'), trigger: 'blur' } { required: true, message: this.$t('pages.emojiManagement.validation.required'), trigger: 'blur' }
@ -389,10 +395,16 @@ export default {
created() { created() {
const that = this const that = this
const querySystem = this.permissionsSysOriginPlatforms[0] const querySystem = this.permissionsSysOriginPlatforms[0]
if (!querySystem) { const sysOrigin = this.initialGroup && this.initialGroup.sysOrigin
? this.initialGroup.sysOrigin
: querySystem && querySystem.value
if (!sysOrigin) {
return return
} }
that.listQuery.sysOrigin = querySystem.value that.listQuery.sysOrigin = sysOrigin
that.listQuery.groupId = this.initialGroup && this.initialGroup.id
? this.initialGroup.id
: ''
that.renderData() that.renderData()
that.loadGroupList() that.loadGroupList()
}, },

View File

@ -7,10 +7,10 @@
:append-to-body="true" :append-to-body="true"
:before-close="handleClose" :before-close="handleClose"
:close-on-click-modal="false" :close-on-click-modal="false"
width="450px" width="720px"
> >
<div v-loading="submitLoading"> <div v-loading="submitLoading">
<el-form ref="form" :model="form" label-width="110px" :rules="formRules" style="margin-right: 20px;"> <el-form ref="form" :model="form" label-width="150px" :rules="formRules" style="margin-right: 20px;">
<el-form-item <el-form-item
v-if="!form.id" v-if="!form.id"
:label="$t('pages.emojiManagement.common.system')" :label="$t('pages.emojiManagement.common.system')"
@ -77,6 +77,29 @@
<el-input v-model.trim="form.sort" v-number :placeholder="$t('pages.emojiManagement.group.placeholder.weight')" /> <el-input v-model.trim="form.sort" v-number :placeholder="$t('pages.emojiManagement.group.placeholder.weight')" />
</el-form-item> </el-form-item>
<el-divider content-position="left">{{ $t('pages.emojiManagement.group.form.accessRules') }}</el-divider>
<el-form-item :label="$t('pages.emojiManagement.group.form.vipRange')">
<el-input-number v-model="form.minVipLevel" :min="0" :max="6" controls-position="right" />
<span class="range-separator">-</span>
<el-input-number v-model="form.maxVipLevel" :min="0" :max="6" controls-position="right" />
</el-form-item>
<el-form-item :label="$t('pages.emojiManagement.group.form.wealthRange')">
<el-input-number v-model="form.minWealthLevel" :min="0" :max="9999" controls-position="right" />
<span class="range-separator">-</span>
<el-input-number v-model="form.maxWealthLevel" :min="0" :max="9999" controls-position="right" />
</el-form-item>
<el-form-item :label="$t('pages.emojiManagement.group.form.charmRange')">
<el-input-number v-model="form.minCharmLevel" :min="0" :max="9999" controls-position="right" />
<span class="range-separator">-</span>
<el-input-number v-model="form.maxCharmLevel" :min="0" :max="9999" controls-position="right" />
</el-form-item>
<el-alert
:title="$t('pages.emojiManagement.group.form.rangeHelp')"
type="info"
:closable="false"
show-icon
/>
<el-form-item> <el-form-item>
<el-button type="primary" @click="submitForm()">{{ $t('common.save') }}</el-button> <el-button type="primary" @click="submitForm()">{{ $t('common.save') }}</el-button>
<el-button @click="handleClose()">{{ $t('common.cancel') }}</el-button> <el-button @click="handleClose()">{{ $t('common.cancel') }}</el-button>
@ -117,7 +140,13 @@ export default {
sysOrigin: '', sysOrigin: '',
shelfStatus: '', shelfStatus: '',
ktvStatus: '', ktvStatus: '',
amount: '' amount: '',
minVipLevel: 0,
maxVipLevel: 0,
minWealthLevel: 0,
maxWealthLevel: 0,
minCharmLevel: 0,
maxCharmLevel: 0
}, },
formRules: { formRules: {
cover: commonRules, cover: commonRules,
@ -155,6 +184,17 @@ export default {
} }
this.form = deepClone(newVal) this.form = deepClone(newVal)
const accessLevelFields = [
'minVipLevel',
'maxVipLevel',
'minWealthLevel',
'maxWealthLevel',
'minCharmLevel',
'maxCharmLevel'
]
accessLevelFields.forEach(field => {
this.$set(this.form, field, Number(newVal[field] || 0))
})
if (this.form.cover) { if (this.form.cover) {
this.coverFileList = getElementUiUploadFile(this.form.cover) this.coverFileList = getElementUiUploadFile(this.form.cover)
} }
@ -187,6 +227,11 @@ export default {
console.error('error submit!!') console.error('error submit!!')
return false return false
} }
// 0 0 .
if (!that.validateAccessLevelRanges()) {
that.$message.error(that.$t('pages.emojiManagement.validation.invalidLevelRange'))
return false
}
that.submitLoading = true that.submitLoading = true
addOrUpdateGroup(that.form).then(res => { addOrUpdateGroup(that.form).then(res => {
that.submitLoading = false that.submitLoading = false
@ -198,10 +243,23 @@ export default {
}) })
return return
}) })
},
validateAccessLevelRanges() {
return [
[this.form.minVipLevel, this.form.maxVipLevel],
[this.form.minWealthLevel, this.form.maxWealthLevel],
[this.form.minCharmLevel, this.form.maxCharmLevel]
].every(([minLevel, maxLevel]) => {
const min = Number(minLevel || 0)
const max = Number(maxLevel || 0)
return min >= 0 && max >= 0 && (max === 0 || min <= max)
})
} }
} }
} }
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.range-separator {
margin: 0 10px;
}
</style> </style>

View File

@ -105,16 +105,32 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('pages.emojiManagement.group.table.vipRange')" align="center" width="120">
<template slot-scope="scope">
{{ formatLevelRange(scope.row.minVipLevel, scope.row.maxVipLevel, 'VIP') }}
</template>
</el-table-column>
<el-table-column :label="$t('pages.emojiManagement.group.table.wealthRange')" align="center" width="120">
<template slot-scope="scope">
{{ formatLevelRange(scope.row.minWealthLevel, scope.row.maxWealthLevel) }}
</template>
</el-table-column>
<el-table-column :label="$t('pages.emojiManagement.group.table.charmRange')" align="center" width="120">
<template slot-scope="scope">
{{ formatLevelRange(scope.row.minCharmLevel, scope.row.maxCharmLevel) }}
</template>
</el-table-column>
<el-table-column :label="$t('pages.emojiManagement.common.sort')" prop="sort" align="center" /> <el-table-column :label="$t('pages.emojiManagement.common.sort')" prop="sort" align="center" />
<el-table-column :label="$t('pages.emojiManagement.common.createdAt')" prop="createTime" width="200" align="center"> <el-table-column :label="$t('pages.emojiManagement.common.createdAt')" prop="createTime" width="200" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.createTime | dateFormat }} {{ scope.row.createTime | dateFormat }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('pages.emojiManagement.common.actions')" fixed="right" align="center" width="100"> <el-table-column :label="$t('pages.emojiManagement.common.actions')" fixed="right" align="center" width="170">
<template slot-scope="scope"> <template slot-scope="scope">
<div> <div>
<el-button type="text" @click.native="clickEdit(scope.row)">{{ $t('pages.emojiManagement.common.edit') }}</el-button> <el-button type="text" @click.native="clickEdit(scope.row)">{{ $t('pages.emojiManagement.common.edit') }}</el-button>
<el-button type="text" @click.native="clickManageResources(scope.row)">{{ $t('pages.emojiManagement.group.table.manageResources') }}</el-button>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -210,6 +226,23 @@ export default {
this.thisClickRow = Object.assign({}, row) this.thisClickRow = Object.assign({}, row)
this.formEditVisiable = true this.formEditVisiable = true
}, },
clickManageResources(row) {
this.$emit('manage-resources', Object.assign({}, row))
},
formatLevelRange(minLevel, maxLevel, prefix = '') {
const min = Number(minLevel || 0)
const max = Number(maxLevel || 0)
const value = min === 0 && max === 0
? this.$t('pages.emojiManagement.options.unrestricted')
: max === 0
? `${min}+`
: min === 0
? `${max}`
: `${min}-${max}`
return prefix && value !== this.$t('pages.emojiManagement.options.unrestricted')
? `${prefix}${value}`
: value
},
formEditSuccess() { formEditSuccess() {
this.handleSearch() this.handleSearch()
this.formEditVisiable = false this.formEditVisiable = false

View File

@ -2,7 +2,11 @@
<div class="app-container"> <div class="app-container">
<el-tabs v-model="activeName"> <el-tabs v-model="activeName">
<el-tab-pane v-for="item in tables" :key="item.name" :label="$t(item.titleKey)" :name="item.component" /> <el-tab-pane v-for="item in tables" :key="item.name" :label="$t(item.titleKey)" :name="item.component" />
<component :is="activeName" /> <component
:is="activeName"
v-bind="activeComponentProps"
@manage-resources="handleManageResources"
/>
</el-tabs> </el-tabs>
</div> </div>
</template> </template>
@ -15,6 +19,7 @@ export default {
data() { data() {
return { return {
activeName: 'EmojiGroup', activeName: 'EmojiGroup',
selectedGroup: null,
tables: [ tables: [
{ {
titleKey: 'pages.emojiManagement.tabs.group', titleKey: 'pages.emojiManagement.tabs.group',
@ -26,6 +31,19 @@ export default {
} }
] ]
} }
},
computed: {
activeComponentProps() {
return this.activeName === 'EmojiConf'
? { initialGroup: this.selectedGroup }
: {}
}
},
methods: {
handleManageResources(group) {
this.selectedGroup = group
this.activeName = 'EmojiConf'
}
} }
} }
</script> </script>