manager 升级到vue3

This commit is contained in:
pikachu1995@126.com
2026-05-25 10:49:09 +08:00
parent e7350899bf
commit 615ee91511
239 changed files with 22907 additions and 30841 deletions

View File

@@ -1,57 +1,80 @@
<template>
<div class="search">
<Card>
<Form
<el-card>
<el-form
ref="searchForm"
:model="searchForm"
inline
:label-width="90"
@keydown.enter.native="handleSearch"
@submit.native.prevent
label-width="90px"
class="search-form"
@keyup.enter="handleSearch"
>
<FormItem label="客户手机号" prop="mobile">
<Input
<el-form-item label="客户手机号" prop="mobile">
<el-input
v-model="searchForm.memberMobile"
clearable
placeholder="请输入客户手机号"
style="width: 220px"
/>
</FormItem>
<FormItem label="规则" prop="ruleKey">
<Select v-model="searchForm.ruleKey" clearable filterable style="width: 220px">
<Option v-for="item in ruleOptions" :key="item.value" :value="item.value">
{{ item.label }}
</Option>
</Select>
</FormItem>
<Button type="primary" icon="ios-search" @click="handleSearch">搜索</Button>
</Form>
</Card>
</el-form-item>
<el-form-item label="规则" prop="ruleKey">
<el-select v-model="searchForm.ruleKey" clearable filterable style="width: 220px">
<el-option
v-for="item in ruleOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
<Card>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
class="mt_10"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
<el-card>
<el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
<el-table-column label="客户手机号" width="140" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row">{{ row.memberMobile || row.mobile || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="规则名称" width="150" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row">{{ row.ruleName || findRuleName(row.ruleKey) || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="变化经验值" width="120">
<template #default="{ row }">
<span v-if="row">{{ formatExperienceValue(row) }}</span>
</template>
</el-table-column>
<el-table-column label="经验值限额" width="120">
<template #default="{ row }">
<span v-if="row">{{ formatMaxValue(row) }}</span>
</template>
</el-table-column>
<el-table-column label="操作说明" min-width="200" show-overflow-tooltip>
<template #default="{ row }">
<span v-if="row">{{ row.content || row.remark || row.description || "-" }}</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="170" />
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
</div>
</template>
@@ -86,67 +109,20 @@ export default {
memberMobile: "",
ruleKey: "",
},
columns: [
{
title: "客户手机号",
key: "memberMobile",
width: 140,
tooltip: true,
render: (h, params) => {
const v = params.row.memberMobile || params.row.mobile || "-";
return h("span", v);
},
},
{
title: "规则名称",
key: "ruleName",
width: 150,
tooltip: true,
render: (h, params) => {
const text = params.row.ruleName || this.findRuleName(params.row.ruleKey) || "-";
return h("span", text);
},
},
{
title: "变化经验值",
key: "value",
width: 120,
render: (h, params) => {
const v =
params.row.value ??
params.row.variableExperience ??
params.row.experience ??
params.row.variableValue;
return h("span", v == null ? "-" : v);
},
},
{
title: "经验值限额",
key: "maxValue",
width: 120,
render: (h, params) => {
const v = params.row.maxValue ?? params.row.maxExperience ?? params.row.limitValue;
return h("span", v == null ? "-" : v);
},
},
{
title: "操作说明",
key: "content",
minWidth: 200,
tooltip: true,
render: (h, params) => {
const text = params.row.content || params.row.remark || params.row.description || "-";
return h("span", text);
},
},
{ title: "创建时间", key: "createTime", width: 170 },
],
};
},
mounted() {
this.init();
},
methods: {
formatExperienceValue(row) {
const v = row.value ?? row.variableExperience ?? row.experience ?? row.variableValue;
return v == null ? "-" : v;
},
formatMaxValue(row) {
const v = row.maxValue ?? row.maxExperience ?? row.limitValue;
return v == null ? "-" : v;
},
findRuleName(ruleKey) {
const hit = this.ruleOptions.find((item) => item.value === ruleKey);
return hit ? hit.label : "";

View File

@@ -1,10 +1,226 @@
<template>
<div class="experience-setting">
<Card>
<Form :label-width="120" label-position="right">
<Table :loading="loading" :columns="columns" :data="form.items" class="mt_10 experience-table"></Table>
<FormItem label="经验值说明" style="margin-top: 16px" class="desc-item">
<Input
<el-card>
<el-form label-width="120px" label-position="right">
<el-table
v-loading="loading"
:data="form.items"
class="mt_10 experience-table"
style="width: 100%"
>
<el-table-column label="是否开启" width="90" align="center">
<template #default="{ row, $index }">
<el-checkbox
v-if="row"
:model-value="!!form.items[$index].enabled"
@change="(checked) => updateRuleEnabled($index, checked)"
/>
</template>
</el-table-column>
<el-table-column prop="ruleName" label="类型" width="160" />
<el-table-column label="经验值(1-100)" min-width="700">
<template #default="{ row, $index }">
<template v-if="row">
<div v-if="row.ruleKey === 'REGISTER'" class="rule-cell">
<div class="rule-row">
<span class="rule-label">获得经验值:</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">会员注册成功后可获得经验值</div>
</div>
<div v-else-if="row.ruleKey === 'SHARE'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<div class="rule-inline">
<span class="rule-label">分享商品详情页获得经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-inline">
<span class="rule-label">可获得经验值限额</span>
<el-input
:model-value="formatInputValue(form.items[$index].maxValue)"
style="width: 120px"
@input="(val) => updateRuleMaxValue($index, val)"
@blur="() => commitRuleMaxValue($index)"
/>
</div>
</div>
<div class="rule-tip">会员分享商城页面可获得的经验值</div>
</div>
<div v-else-if="row.ruleKey === 'COMMENT'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">对已购买商品完成提交评论获得经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">仅针对评论字数大于30字的评论进行发放</div>
</div>
<div v-else-if="row.ruleKey === 'FOLLOW_STORE'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<div class="rule-inline">
<span class="rule-label">获得经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-inline">
<span class="rule-label">可获得经验值限额</span>
<el-input
:model-value="formatInputValue(form.items[$index].maxValue)"
style="width: 120px"
@input="(val) => updateRuleMaxValue($index, val)"
@blur="() => commitRuleMaxValue($index)"
/>
</div>
</div>
<div class="rule-tip">关注店铺可获得经验值每个客户D相同店铺仅第一次关注可进行获得</div>
</div>
<div v-else-if="row.ruleKey === 'PROFILE'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">获得经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">完善个人基本信息可获得经验值每个会员仅可获得一次</div>
</div>
<div v-else-if="row.ruleKey === 'BIND_WECHAT'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">获取经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">绑定微信成功获得经验值每个会员仅可获得一次</div>
</div>
<div v-else-if="row.ruleKey === 'ADD_ADDRESS'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">获取经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">添加收货地址后获得经验值每个会员仅可获得一次</div>
</div>
<div v-else-if="row.ruleKey === 'SHARE_REGISTER'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<div class="rule-inline">
<span class="rule-label">获取的经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-inline">
<span class="rule-label">可获得经验值限额</span>
<el-input
:model-value="formatInputValue(form.items[$index].maxValue)"
style="width: 120px"
@input="(val) => updateRuleMaxValue($index, val)"
@blur="() => commitRuleMaxValue($index)"
/>
</div>
</div>
<div class="rule-tip">仅被注册成功后才可获得相应奖励经验值</div>
</div>
<div v-else-if="row.ruleKey === 'SHARE_BUY'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<div class="rule-inline">
<span class="rule-label">获取的经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-inline">
<span class="rule-label">可获得经验值限额</span>
<el-input
:model-value="formatInputValue(form.items[$index].maxValue)"
style="width: 120px"
@input="(val) => updateRuleMaxValue($index, val)"
@blur="() => commitRuleMaxValue($index)"
/>
</div>
</div>
<div class="rule-tip">仅被购买成功后才可获得相应奖励经验值</div>
</div>
<div v-else-if="row.ruleKey === 'SIGN_IN'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">获取的经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">客户每日签到后可获的经验值</div>
</div>
<div v-else-if="row.ruleKey === 'CONSUME'" class="rule-cell">
<div class="rule-row rule-row-wrap">
<span class="rule-label">1元获取经验值</span>
<el-input
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</div>
<div class="rule-tip">客户消费1元可获取经验值向下取整</div>
</div>
<el-input
v-else
:model-value="formatInputValue(form.items[$index].value)"
style="width: 120px"
@input="(val) => updateRuleValue($index, val)"
@blur="() => commitRuleValue($index)"
/>
</template>
</template>
</el-table-column>
</el-table>
<el-form-item label="经验值说明" style="margin-top: 16px" class="desc-item">
<el-input
v-model="form.description"
type="textarea"
:rows="4"
@@ -12,12 +228,12 @@
show-word-limit
placeholder="请输入经验值说明"
/>
</FormItem>
<FormItem>
<Button type="primary" :loading="submitLoading" @click="submit">保存</Button>
</FormItem>
</Form>
</Card>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitLoading" @click="submit">保存</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</template>
@@ -58,340 +274,33 @@ export default {
loading: false,
submitLoading: false,
form: defaultForm(),
columns: [
{
title: "是否开启",
key: "enabled",
width: 90,
align: "center",
render: (h, params) => {
return h("Checkbox", {
props: { value: !!this.form.items[params.index].enabled },
on: {
"on-change": (checked) => {
this.updateRuleEnabled(params.index, checked);
},
},
});
},
},
{
title: "类型",
key: "ruleName",
width: 160,
},
{
title: "经验值(1-100)",
key: "value",
minWidth: 700,
render: (h, params) => {
const row = this.form.items[params.index] || {};
const inputNode = h("Input", {
style: { width: "120px" },
props: {
value: row.value == null ? "" : String(row.value),
number: true,
},
on: {
input: (val) => {
this.updateRuleValue(params.index, val);
},
"on-change": (v) => {
this.updateRuleValue(params.index, v);
},
"on-blur": () => {
this.commitRuleValue(params.index);
},
},
});
const maxInputNode = h("Input", {
style: { width: "120px" },
props: {
value: row.maxValue == null ? "" : String(row.maxValue),
number: true,
},
on: {
input: (val) => {
this.updateRuleMaxValue(params.index, val);
},
"on-change": (v) => {
this.updateRuleMaxValue(params.index, v);
},
"on-blur": () => {
this.commitRuleMaxValue(params.index);
},
},
});
if (params.row.ruleKey === "REGISTER") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "获得经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"会员注册成功后可获得经验值"
),
]
);
}
if (params.row.ruleKey === "SHARE") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", marginRight: "16px" } },
[h("span", { style: { marginRight: "8px" } }, "分享商品详情页获得经验值:"), inputNode]
),
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "可获得经验值限额:"), maxInputNode]
),
]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"会员分享商城页面可获得的经验值"
),
]
);
}
if (params.row.ruleKey === "COMMENT") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "对已购买商品完成提交评论获得经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"仅针对评论字数大于30字的评论进行发放"
),
]
);
}
if (params.row.ruleKey === "FOLLOW_STORE") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", marginRight: "16px" } },
[h("span", { style: { marginRight: "8px" } }, "获得经验值:"), inputNode]
),
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "可获得经验值限额:"), maxInputNode]
),
]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"关注店铺可获得经验值每个客户D相同店铺仅第一次关注可进行获得"
),
]
);
}
if (params.row.ruleKey === "PROFILE") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "获得经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"完善个人基本信息可获得经验值,每个会员仅可获得一次"
),
]
);
}
if (params.row.ruleKey === "BIND_WECHAT") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "获取经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"绑定微信成功获得经验值,每个会员仅可获得一次"
),
]
);
}
if (params.row.ruleKey === "ADD_ADDRESS") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "获取经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"添加收货地址后获得经验值,每个会员仅可获得一次"
),
]
);
}
if (params.row.ruleKey === "SHARE_REGISTER") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", marginRight: "16px" } },
[h("span", { style: { marginRight: "8px" } }, "获取的经验值:"), inputNode]
),
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "可获得经验值限额:"), maxInputNode]
),
]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"仅被注册成功后才可获得相应奖励经验值"
),
]
);
}
if (params.row.ruleKey === "SHARE_BUY") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", marginRight: "16px" } },
[h("span", { style: { marginRight: "8px" } }, "获取的经验值:"), inputNode]
),
h(
"div",
{ style: { display: "flex", alignItems: "center" } },
[h("span", { style: { marginRight: "8px" } }, "可获得经验值限额:"), maxInputNode]
),
]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"仅被购买成功后才可获得相应奖励经验值"
),
]
);
}
if (params.row.ruleKey === "SIGN_IN") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "获取的经验值:"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"客户每日签到后可获的经验值"
),
]
);
}
if (params.row.ruleKey === "CONSUME") {
return h(
"div",
{ style: { display: "flex", flexDirection: "column", alignItems: "flex-start" } },
[
h(
"div",
{ style: { display: "flex", alignItems: "center", flexWrap: "wrap" } },
[h("span", { style: { marginRight: "8px" } }, "1元获取经验值"), inputNode]
),
h(
"div",
{ style: { marginTop: "6px", color: "#808695", fontSize: "12px" } },
"客户消费1元可获取经验值向下取整"
),
]
);
}
return inputNode;
},
},
],
};
},
mounted() {
this.loadData();
},
methods: {
formatInputValue(v) {
return v == null ? "" : String(v);
},
getRawInputValue(v) {
return v && v.target ? v.target.value : v;
},
updateRuleEnabled(index, enabled) {
const item = this.form.items[index] || {};
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
enabled: !!enabled,
});
};
},
updateRuleValue(index, v) {
const raw = this.getRawInputValue(v);
const next = Number(raw);
const item = this.form.items[index] || {};
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
value: Number.isFinite(next) ? next : null,
});
};
},
commitRuleValue(index) {
const item = this.form.items[index] || {};
@@ -402,41 +311,41 @@ export default {
else if (next > 100) value = 100;
else value = Math.floor(next);
}
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
value,
});
};
},
updateRuleMaxValue(index, v) {
const raw = this.getRawInputValue(v);
const item = this.form.items[index] || {};
if (raw == null || raw === "") {
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
maxValue: null,
});
};
return;
}
const next = Number(raw);
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
maxValue: Number.isFinite(next) ? next : null,
});
};
},
commitRuleMaxValue(index) {
const item = this.form.items[index] || {};
if (item.maxValue == null || item.maxValue === "") {
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
maxValue: null,
});
};
return;
}
const next = Number(item.maxValue);
this.$set(this.form.items, index, {
this.form.items[index] = {
...item,
maxValue: Number.isFinite(next) && next >= 1 ? Math.floor(next) : null,
});
};
},
normalizeConfig(val) {
if (!val) return {};
@@ -535,21 +444,52 @@ export default {
padding: 2px 0;
}
::v-deep .experience-table .ivu-table th {
.rule-cell {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.rule-row {
display: flex;
align-items: center;
}
.rule-row-wrap {
flex-wrap: wrap;
}
.rule-inline {
display: flex;
align-items: center;
margin-right: 16px;
}
.rule-label {
margin-right: 8px;
}
.rule-tip {
margin-top: 6px;
color: #808695;
font-size: 12px;
}
:deep(.experience-table .el-table__header th) {
background: #fafbfc;
}
::v-deep .experience-table .ivu-table td {
:deep(.experience-table .el-table__body td) {
padding-top: 12px;
padding-bottom: 12px;
}
::v-deep .experience-table .ivu-table-cell {
:deep(.experience-table .cell) {
font-size: 13px;
line-height: 1.7;
}
::v-deep .desc-item .ivu-form-item-label {
:deep(.desc-item .el-form-item__label) {
font-size: 16px;
font-weight: 600;
}

View File

@@ -1,120 +1,180 @@
<template>
<div class="search">
<Card>
<Row class="operation padding-row">
<Button type="primary" @click="openAdd">添加客户等级</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
class="mt_10"
></Table>
</Card>
<el-card>
<div class="operation padding-row">
<el-button type="primary" @click="openAdd">添加客户等级</el-button>
</div>
<Modal v-model="addFlag" title="添加客户等级" width="720" :z-index="950" :mask-closable="false">
<Form ref="addForm" :model="formAdd" :rules="rules" :label-width="110">
<FormItem label="等级名称" prop="gradeName">
<Input v-model="formAdd.gradeName" maxlength="50" placeholder="请输入等级名称" />
</FormItem>
<FormItem label="是否默认" prop="isDefault">
<RadioGroup v-model="formAdd.isDefault">
<Radio :label="true"></Radio>
<Radio :label="false"></Radio>
</RadioGroup>
</FormItem>
<FormItem label="等级图标" prop="gradeImage">
<upload-pic-input v-model="formAdd.gradeImage"></upload-pic-input>
</FormItem>
<FormItem label="等级背景图" prop="gradeBackground">
<upload-pic-input v-model="formAdd.gradeBackground"></upload-pic-input>
</FormItem>
<FormItem label="字体颜色" prop="gradeFontColor">
<Input v-model="formAdd.gradeFontColor" maxlength="20" placeholder="如:#333333" />
</FormItem>
<FormItem label="所需经验值" prop="requiredExperience">
<InputNumber v-model="formAdd.requiredExperience" :min="1" :precision="0" style="width: 220px"></InputNumber>
</FormItem>
<FormItem label="等级排序" prop="gradeSort">
<InputNumber v-model="formAdd.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px"></InputNumber>
</FormItem>
<FormItem label="等级开关" prop="gradeState">
<RadioGroup v-model="formAdd.gradeState">
<Radio label="OPEN">开启</Radio>
<Radio label="CLOSE">关闭</Radio>
</RadioGroup>
</FormItem>
<FormItem label="关联权益" prop="benefitIds">
<Select
:value="addBenefitOrder"
<el-table v-loading="loading" border :data="data" class="mt_10" style="width: 100%">
<el-table-column prop="gradeName" label="等级名称" width="130" show-overflow-tooltip />
<el-table-column label="默认等级" width="95">
<template #default="{ row }">
<el-tag v-if="row" :type="row.isDefault === true ? 'success' : 'info'">
{{ row.isDefault === true ? "" : "" }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="等级图标" width="100">
<template #default="{ row }">
<span v-if="row && !row.gradeImage">-</span>
<img
v-else-if="row"
:src="row.gradeImage"
alt="等级图标"
style="width: 48px; height: 48px; object-fit: contain; border: 1px solid #dcdee2; border-radius: 4px; background: #fff"
/>
</template>
</el-table-column>
<el-table-column label="等级背景图" width="120">
<template #default="{ row }">
<span v-if="row && !row.gradeBackground">-</span>
<img
v-else-if="row"
:src="row.gradeBackground"
alt="等级背景图"
style="width: 64px; height: 40px; object-fit: cover; border: 1px solid #dcdee2; border-radius: 4px; background: #fff"
/>
</template>
</el-table-column>
<el-table-column prop="requiredExperience" label="所需经验值" width="110" />
<el-table-column prop="gradeSort" label="等级排序" width="95" />
<el-table-column label="状态" width="118" align="center">
<template #default="{ row }">
<el-switch
v-if="row"
:model-value="row.gradeState === 'OPEN'"
inline-prompt
active-text="开启"
inactive-text="关闭"
:loading="!!row._gradeStateLoading"
@change="(checked) => onGradeStateSwitch(row, checked)"
/>
</template>
</el-table-column>
<el-table-column label="操作" width="130" align="center">
<template #default="{ row }">
<div v-if="row" class="ops" style="display: flex; justify-content: center">
<a class="link-text" @click="openEdit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</div>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog v-model="addFlag" title="添加客户等级" width="720px" :z-index="950" :close-on-click-modal="false" destroy-on-close>
<el-form ref="addForm" :model="formAdd" :rules="rules" label-width="110px">
<el-form-item label="等级名称" prop="gradeName">
<el-input v-model="formAdd.gradeName" maxlength="50" placeholder="请输入等级名称" />
</el-form-item>
<el-form-item label="是否默认" prop="isDefault">
<el-radio-group v-model="formAdd.isDefault">
<el-radio :value="true"></el-radio>
<el-radio :value="false"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="等级图标" prop="gradeImage">
<upload-pic-input v-model="formAdd.gradeImage" />
</el-form-item>
<el-form-item label="等级背景图" prop="gradeBackground">
<upload-pic-input v-model="formAdd.gradeBackground" />
</el-form-item>
<el-form-item label="字体颜色" prop="gradeFontColor">
<el-input v-model="formAdd.gradeFontColor" maxlength="20" placeholder="如:#333333" />
</el-form-item>
<el-form-item label="所需经验值" prop="requiredExperience">
<el-input-number v-model="formAdd.requiredExperience" :min="1" :precision="0" style="width: 220px" />
</el-form-item>
<el-form-item label="等级排序" prop="gradeSort">
<el-input-number v-model="formAdd.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px" />
</el-form-item>
<el-form-item label="等级开关" prop="gradeState">
<el-radio-group v-model="formAdd.gradeState">
<el-radio value="OPEN">开启</el-radio>
<el-radio value="CLOSE">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="关联权益" prop="benefitIds">
<el-select
:model-value="addBenefitOrder"
multiple
filterable
placeholder="请选择客户权益"
style="width: 100%"
@on-change="onAddBenefitIdsChange"
@change="onAddBenefitIdsChange"
>
<Option v-for="b in benefitOptions" :key="b.id" :value="String(b.id)">{{ benefitOptionLabel(b) }}</Option>
</Select>
</FormItem>
</Form>
<div slot="footer">
<Button @click="addFlag = false">取消</Button>
<Button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</Button>
</div>
</Modal>
<el-option
v-for="b in benefitOptions"
:key="b.id"
:value="String(b.id)"
:label="benefitOptionLabel(b)"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="addFlag = false">取消</el-button>
<el-button type="primary" :loading="submitAddLoading" @click="submitAdd">确定</el-button>
</template>
</el-dialog>
<Modal v-model="editFlag" title="编辑客户等级" width="720" :z-index="950" :mask-closable="false">
<Form ref="editForm" :model="formEdit" :rules="rules" :label-width="110">
<Input v-model="formEdit.id" v-show="false" />
<FormItem label="等级名称" prop="gradeName">
<Input v-model="formEdit.gradeName" maxlength="50" placeholder="请输入等级名称" />
</FormItem>
<FormItem label="是否默认" prop="isDefault">
<RadioGroup v-model="formEdit.isDefault">
<Radio :label="true"></Radio>
<Radio :label="false"></Radio>
</RadioGroup>
</FormItem>
<FormItem label="等级图标" prop="gradeImage">
<upload-pic-input v-model="formEdit.gradeImage"></upload-pic-input>
</FormItem>
<FormItem label="等级背景图" prop="gradeBackground">
<upload-pic-input v-model="formEdit.gradeBackground"></upload-pic-input>
</FormItem>
<FormItem label="字体颜色" prop="gradeFontColor">
<Input v-model="formEdit.gradeFontColor" maxlength="20" placeholder="如:#333333" />
</FormItem>
<FormItem label="所需经验值" prop="requiredExperience">
<InputNumber v-model="formEdit.requiredExperience" :min="1" :precision="0" style="width: 220px"></InputNumber>
</FormItem>
<FormItem label="等级排序" prop="gradeSort">
<InputNumber v-model="formEdit.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px"></InputNumber>
</FormItem>
<FormItem label="等级开关" prop="gradeState">
<RadioGroup v-model="formEdit.gradeState">
<Radio label="OPEN">开启</Radio>
<Radio label="CLOSE">关闭</Radio>
</RadioGroup>
</FormItem>
<FormItem label="关联权益" prop="benefitIds">
<Select
:value="editBenefitOrder"
<el-dialog v-model="editFlag" title="编辑客户等级" width="720px" :z-index="950" :close-on-click-modal="false" destroy-on-close>
<el-form ref="editForm" :model="formEdit" :rules="rules" label-width="110px">
<el-input v-model="formEdit.id" style="display: none" />
<el-form-item label="等级名称" prop="gradeName">
<el-input v-model="formEdit.gradeName" maxlength="50" placeholder="请输入等级名称" />
</el-form-item>
<el-form-item label="是否默认" prop="isDefault">
<el-radio-group v-model="formEdit.isDefault">
<el-radio :value="true"></el-radio>
<el-radio :value="false"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="等级图标" prop="gradeImage">
<upload-pic-input v-model="formEdit.gradeImage" />
</el-form-item>
<el-form-item label="等级背景图" prop="gradeBackground">
<upload-pic-input v-model="formEdit.gradeBackground" />
</el-form-item>
<el-form-item label="字体颜色" prop="gradeFontColor">
<el-input v-model="formEdit.gradeFontColor" maxlength="20" placeholder="如:#333333" />
</el-form-item>
<el-form-item label="所需经验值" prop="requiredExperience">
<el-input-number v-model="formEdit.requiredExperience" :min="1" :precision="0" style="width: 220px" />
</el-form-item>
<el-form-item label="等级排序" prop="gradeSort">
<el-input-number v-model="formEdit.gradeSort" :min="1" :max="9999" :precision="0" style="width: 220px" />
</el-form-item>
<el-form-item label="等级开关" prop="gradeState">
<el-radio-group v-model="formEdit.gradeState">
<el-radio value="OPEN">开启</el-radio>
<el-radio value="CLOSE">关闭</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="关联权益" prop="benefitIds">
<el-select
:model-value="editBenefitOrder"
multiple
filterable
placeholder="请选择客户权益"
style="width: 100%"
@on-change="onEditBenefitIdsChange"
@change="onEditBenefitIdsChange"
>
<Option v-for="b in benefitOptions" :key="b.id" :value="String(b.id)">{{ benefitOptionLabel(b) }}</Option>
</Select>
</FormItem>
</Form>
<div slot="footer">
<Button @click="editFlag = false">取消</Button>
<Button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</Button>
</div>
</Modal>
<el-option
v-for="b in benefitOptions"
:key="b.id"
:value="String(b.id)"
:label="benefitOptionLabel(b)"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="editFlag = false">取消</el-button>
<el-button type="primary" :loading="submitEditLoading" @click="submitEdit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -135,7 +195,6 @@ const buildDefaultForm = () => ({
benefitIds: "",
});
/** 权益多选:新勾选追加到末尾,取消勾选移除,保持已有顺序 */
function syncOrderedBenefitIds(prevOrder, selected) {
const sel = Array.isArray(selected) ? selected.map((id) => String(id)) : [];
const out = [];
@@ -157,144 +216,6 @@ export default {
data() {
return {
loading: true,
columns: [
{
title: "等级名称",
key: "gradeName",
width: 130,
tooltip: true,
},
{
title: "默认等级",
key: "isDefault",
width: 95,
render: (h, params) => {
const yes = params.row.isDefault === true;
return h(
"Tag",
{
props: {
color: yes ? "success" : "default",
},
},
yes ? "是" : "否"
);
},
},
{
title: "等级图标",
key: "gradeImage",
width: 100,
render: (h, params) => {
if (!params.row.gradeImage) return h("span", "-");
return h("img", {
attrs: {
src: params.row.gradeImage,
alt: "等级图标",
},
style: {
width: "48px",
height: "48px",
objectFit: "contain",
border: "1px solid #dcdee2",
borderRadius: "4px",
background: "#fff",
},
});
},
},
{
title: "等级背景图",
key: "gradeBackground",
width: 120,
render: (h, params) => {
if (!params.row.gradeBackground) return h("span", "-");
return h("img", {
attrs: {
src: params.row.gradeBackground,
alt: "等级背景图",
},
style: {
width: "64px",
height: "40px",
objectFit: "cover",
border: "1px solid #dcdee2",
borderRadius: "4px",
background: "#fff",
},
});
},
},
{
title: "所需经验值",
key: "requiredExperience",
width: 110,
},
{
title: "等级排序",
key: "gradeSort",
width: 95,
},
{
title: "状态",
key: "gradeState",
width: 118,
align: "center",
render: (h, params) => {
const row = params.row;
return h("i-switch", {
props: {
value: row.gradeState === "OPEN",
size: "large",
loading: !!row._gradeStateLoading,
},
on: {
"on-change": (checked) => {
this.onGradeStateSwitch(row, checked);
},
},
}, [
h("span", { slot: "open" }, "开启"),
h("span", { slot: "close" }, "关闭"),
]);
},
},
{
title: "操作",
key: "action",
align: "center",
width: 130,
render: (h, params) => {
const linkStyle = {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
};
const sep = h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
);
return h(
"div",
{ class: "ops", style: { display: "flex", justifyContent: "center" } },
[
h(
"a",
{ style: linkStyle, on: { click: () => this.openEdit(params.row) } },
"编辑"
),
sep,
h(
"a",
{ style: linkStyle, on: { click: () => this.remove(params.row) } },
"删除"
),
]
);
},
},
],
data: [],
addFlag: false,
editFlag: false,
@@ -302,7 +223,6 @@ export default {
submitEditLoading: false,
formAdd: buildDefaultForm(),
formEdit: buildDefaultForm(),
/** 关联权益 id 顺序(与 benefitIds 一致) */
addBenefitOrder: [],
editBenefitOrder: [],
benefitOptions: [],
@@ -506,19 +426,19 @@ export default {
title: "提示",
content: `<p>确定${text}该客户等级?</p>`,
onOk: () => {
this.$set(row, "_gradeStateLoading", true);
row._gradeStateLoading = true;
return API_Member.updateMemberGradeState(row.id, nextState)
.then((res) => {
this.$set(row, "_gradeStateLoading", false);
row._gradeStateLoading = false;
if (res && res.success) {
this.$Message.success(`${text}成功`);
this.$set(row, "gradeState", nextState);
row.gradeState = nextState;
} else {
this.getData();
}
})
.catch(() => {
this.$set(row, "_gradeStateLoading", false);
row._gradeStateLoading = false;
});
},
});
@@ -545,3 +465,15 @@ export default {
},
};
</script>
<style scoped>
.ops a {
color: #2d8cf0;
cursor: pointer;
text-decoration: none;
}
.ops span {
display: inline-block;
margin: 0 8px;
color: #dcdee2;
}
</style>