mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-07 17:38:55 +08:00
refactor: 四端完成 Options API → script setup + vuex → pinia 迁移
- buyer(95) / seller(169) / im(53) / manager(280) 共 597 个组件全部从 Options API 转换为 <script setup> - 四端 vuex 全部移除,状态管理统一迁移至 pinia 4(stores/ 目录) - util.js 等基础设施去除 vm 首参依赖;全局属性($Message/$filters 等)在 script 内改为具名导入 - 被外部 ref 访问的公共组件均已补齐 defineExpose(清单见各端 MIGRATION-VUE3.md) - 各端 eslint 启用 vue/component-api-style 防止新代码回退 Options API - 四端 yarn build 全部通过;迁移约定与注意事项已更新至各端 MIGRATION-VUE3.md
This commit is contained in:
@@ -76,7 +76,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { UserFilled } from "@element-plus/icons-vue";
|
||||
import shrinkableMenu from "./main-parts/shrinkable-menu/shrinkable-menu.vue";
|
||||
import tagsPageOpened from "./main-parts/tags-page-opened.vue";
|
||||
@@ -87,123 +89,108 @@ import util from "@/libs/util.js";
|
||||
import { getNoticePage, logout } from "@/api/index";
|
||||
import config from "@/config/index.js";
|
||||
import defaultLogo from "@/assets/logo.png";
|
||||
import { setStore } from "@/libs/storage";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
import { useSettingStore } from "@/stores/setting";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
UserFilled,
|
||||
shrinkableMenu,
|
||||
tagsPageOpened,
|
||||
messageTip,
|
||||
circleLoading,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
config,
|
||||
sliceNum: 5,
|
||||
userInfo: "",
|
||||
tipsMessage: "",
|
||||
defaultLogo,
|
||||
domainLogo: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
setting() {
|
||||
return this.$store.state.setting.setting;
|
||||
},
|
||||
loading() {
|
||||
return this.$store.state.app.loading;
|
||||
},
|
||||
menuList() {
|
||||
return this.$store.state.app.menuList || [];
|
||||
},
|
||||
pageTagsList() {
|
||||
return this.$store.state.app.pageOpenedList;
|
||||
},
|
||||
avatarPath() {
|
||||
return localStorage.avatorImgPath;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
const userInfo = JSON.parse(Cookies.get("userInfoManager"));
|
||||
this.userInfo = userInfo;
|
||||
this.checkTag(this.$route.name);
|
||||
this.domainLogo = localStorage.getItem("icon") || this.defaultLogo;
|
||||
const link =
|
||||
document.querySelector("link[rel*='icon']") ||
|
||||
document.createElement("link");
|
||||
link.type = "image/x-icon";
|
||||
link.href = localStorage.getItem("domainIcon");
|
||||
link.rel = "shortcut icon";
|
||||
document.getElementsByTagName("head")[0].appendChild(link);
|
||||
window.document.title = localStorage.getItem("title") + " - 运营后台";
|
||||
getNoticePage({}).then((res) => {
|
||||
if (res.success) {
|
||||
this.tipsMessage = res.result;
|
||||
this.$store.state.notices = res.result;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleClickUserDropdown(name) {
|
||||
if (name === "personalCenter") {
|
||||
util.openNewPage(this, "personal-center");
|
||||
this.$router.push({ name: "personal-center" });
|
||||
} else if (name === "changePass") {
|
||||
util.openNewPage(this, "change-password");
|
||||
this.$router.push({ name: "change_password" });
|
||||
} else if (name === "loginOut") {
|
||||
logout().then(() => {
|
||||
this.$store.commit("logout", this);
|
||||
this.$store.commit("setAdded", false);
|
||||
this.setStore("accessToken", "");
|
||||
this.setStore("refreshToken", "");
|
||||
this.$router.push({ path: "/login" });
|
||||
});
|
||||
}
|
||||
},
|
||||
checkTag(name) {
|
||||
const openpageHasTag = util.findOpenedPageIndex(
|
||||
this.pageTagsList,
|
||||
name,
|
||||
this.$route.path
|
||||
) >= 0;
|
||||
if (!openpageHasTag) {
|
||||
util.openNewPage(
|
||||
this,
|
||||
name,
|
||||
this.$route.params || {},
|
||||
this.$route.query || {}
|
||||
);
|
||||
}
|
||||
},
|
||||
resize() {
|
||||
const currWidth = document.body.clientWidth;
|
||||
const count = currWidth / 300;
|
||||
this.sliceNum = count > 6 ? 6 : count;
|
||||
},
|
||||
onLogoError(e) {
|
||||
if (e?.target && e.target.src !== this.defaultLogo) {
|
||||
e.target.src = this.defaultLogo;
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(to) {
|
||||
this.checkTag(to.name);
|
||||
localStorage.currentPageName = to.name;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
this.resize();
|
||||
window.addEventListener("resize", this.resize);
|
||||
this.$store.commit("setOpenedList");
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener("resize", this.resize);
|
||||
},
|
||||
created() {
|
||||
this.$store.commit("setOpenedList");
|
||||
},
|
||||
};
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
const sliceNum = ref(5);
|
||||
const userInfo = ref("");
|
||||
const tipsMessage = ref("");
|
||||
const domainLogo = ref("");
|
||||
|
||||
const setting = computed(() => settingStore.setting);
|
||||
const loading = computed(() => appStore.loading);
|
||||
const menuList = computed(() => appStore.menuList || []);
|
||||
const pageTagsList = computed(() => appStore.pageOpenedList);
|
||||
const avatarPath = computed(() => localStorage.avatorImgPath);
|
||||
|
||||
function init() {
|
||||
const userInfoData = JSON.parse(Cookies.get("userInfoManager"));
|
||||
userInfo.value = userInfoData;
|
||||
checkTag(route.name);
|
||||
domainLogo.value = localStorage.getItem("icon") || defaultLogo;
|
||||
const link =
|
||||
document.querySelector("link[rel*='icon']") ||
|
||||
document.createElement("link");
|
||||
link.type = "image/x-icon";
|
||||
link.href = localStorage.getItem("domainIcon");
|
||||
link.rel = "shortcut icon";
|
||||
document.getElementsByTagName("head")[0].appendChild(link);
|
||||
window.document.title = localStorage.getItem("title") + " - 运营后台";
|
||||
getNoticePage({}).then((res) => {
|
||||
if (res.success) {
|
||||
tipsMessage.value = res.result;
|
||||
appStore.notices = res.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleClickUserDropdown(name) {
|
||||
if (name === "personalCenter") {
|
||||
util.openNewPage("personal-center");
|
||||
router.push({ name: "personal-center" });
|
||||
} else if (name === "changePass") {
|
||||
util.openNewPage("change-password");
|
||||
router.push({ name: "change_password" });
|
||||
} else if (name === "loginOut") {
|
||||
logout().then(() => {
|
||||
userStore.logout();
|
||||
appStore.setAdded(false);
|
||||
setStore("accessToken", "");
|
||||
setStore("refreshToken", "");
|
||||
router.push({ path: "/login" });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkTag(name) {
|
||||
const openpageHasTag = util.findOpenedPageIndex(
|
||||
pageTagsList.value,
|
||||
name,
|
||||
route.path
|
||||
) >= 0;
|
||||
if (!openpageHasTag) {
|
||||
util.openNewPage(name, route.params || {}, route.query || {});
|
||||
}
|
||||
}
|
||||
|
||||
function resize() {
|
||||
const currWidth = document.body.clientWidth;
|
||||
const count = currWidth / 300;
|
||||
sliceNum.value = count > 6 ? 6 : count;
|
||||
}
|
||||
|
||||
function onLogoError(e) {
|
||||
if (e?.target && e.target.src !== defaultLogo) {
|
||||
e.target.src = defaultLogo;
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => route.name,
|
||||
(name) => {
|
||||
checkTag(name);
|
||||
localStorage.currentPageName = name;
|
||||
}
|
||||
);
|
||||
|
||||
appStore.setOpenedList();
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
resize();
|
||||
window.addEventListener("resize", resize);
|
||||
appStore.setOpenedList();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener("resize", resize);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -19,52 +19,48 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { getAppointmentOrderSettings, saveAppointmentOrderSettings } from "@/api/appointment";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "appointmentOrderSettings",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
saving: false,
|
||||
form: {
|
||||
serviceStartRemindMemberHours: 2,
|
||||
inStoreNoShowAutoCompleteHours: 24,
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadData();
|
||||
},
|
||||
methods: {
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
getAppointmentOrderSettings()
|
||||
.then((res) => {
|
||||
if (res.success && res.result) {
|
||||
this.form = { ...this.form, ...res.result };
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleSave() {
|
||||
this.saving = true;
|
||||
saveAppointmentOrderSettings(this.form)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "appointmentOrderSettings" });
|
||||
|
||||
const loading = ref(false);
|
||||
const saving = ref(false);
|
||||
const form = ref({
|
||||
serviceStartRemindMemberHours: 2,
|
||||
inStoreNoShowAutoCompleteHours: 24,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
getAppointmentOrderSettings()
|
||||
.then((res) => {
|
||||
if (res.success && res.result) {
|
||||
form.value = { ...form.value, ...res.result };
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function handleSave() {
|
||||
saving.value = true;
|
||||
saveAppointmentOrderSettings(form.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
saving.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -43,9 +43,15 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { getManagerWorkOrderPage } from "@/api/appointment";
|
||||
|
||||
defineOptions({ name: "managerAppointmentWorkOrders" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
{ label: "未完成", value: "OPEN" },
|
||||
{ label: "待领取", value: "PENDING_CLAIM" },
|
||||
@@ -57,52 +63,45 @@ const STATUS_OPTIONS = [
|
||||
{ label: "逾期未到店", value: "NO_SHOW" },
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "managerAppointmentWorkOrders",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
statusOptions: STATUS_OPTIONS,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
orderSn: "",
|
||||
workOrderSn: "",
|
||||
serviceStatus: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const status = this.$route.query.serviceStatus;
|
||||
if (status) {
|
||||
this.searchForm.serviceStatus = status;
|
||||
}
|
||||
this.getData();
|
||||
},
|
||||
methods: {
|
||||
statusText(value) {
|
||||
const found = STATUS_OPTIONS.find((item) => item.value === value);
|
||||
return found ? found.label : value || "—";
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getData();
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
getManagerWorkOrderPage(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result?.records || [];
|
||||
this.total = res.result?.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
const loading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const statusOptions = ref(STATUS_OPTIONS);
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
orderSn: "",
|
||||
workOrderSn: "",
|
||||
serviceStatus: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
const status = route.query.serviceStatus;
|
||||
if (status) {
|
||||
searchForm.value.serviceStatus = status;
|
||||
}
|
||||
getData();
|
||||
});
|
||||
|
||||
function statusText(value) {
|
||||
const found = STATUS_OPTIONS.find((item) => item.value === value);
|
||||
return found ? found.label : value || "—";
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
getManagerWorkOrderPage(searchForm.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result?.records || [];
|
||||
total.value = res.result?.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-card class="change-pass">
|
||||
<template #header>修改密码</template>
|
||||
<el-form
|
||||
ref="editPasswordForm"
|
||||
ref="editPasswordFormRef"
|
||||
:model="editPasswordForm"
|
||||
label-width="100px"
|
||||
label-position="right"
|
||||
@@ -39,80 +39,83 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import SetPassword from "@/components/lili/set-password";
|
||||
import { changePass } from "@/api/index";
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import { md5 } from "@/utils/md5.js";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useUserStore } from "@/stores/user";
|
||||
|
||||
export default {
|
||||
name: "change-password",
|
||||
components: {
|
||||
SetPassword,
|
||||
},
|
||||
data() {
|
||||
const valideRePassword = (rule, value, callback) => {
|
||||
if (value !== this.editPasswordForm.newPassword) {
|
||||
callback(new Error("两次输入密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
savePassLoading: false,
|
||||
editPasswordForm: {
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
rePassword: "",
|
||||
},
|
||||
strength: "",
|
||||
passwordValidate: {
|
||||
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
||||
newPassword: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 6, message: "请至少输入6个字符", trigger: "blur" },
|
||||
{ max: 32, message: "最多输入32个字符", trigger: "blur" },
|
||||
],
|
||||
rePassword: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{ validator: valideRePassword, trigger: "blur" },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
changeInputPass(v, grade, strength) {
|
||||
this.strength = strength;
|
||||
},
|
||||
saveEditPass() {
|
||||
const params = {
|
||||
password: this.md5(this.editPasswordForm.oldPassword),
|
||||
newPassword: this.md5(this.editPasswordForm.newPassword),
|
||||
passStrength: this.md5(this.strength),
|
||||
};
|
||||
this.$refs.editPasswordForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.savePassLoading = true;
|
||||
changePass(params).then((res) => {
|
||||
this.savePassLoading = false;
|
||||
if (res.success) {
|
||||
ElMessageBox.alert("修改密码成功,需重新登录", "修改密码成功", {
|
||||
confirmButtonText: "确定",
|
||||
callback: () => {
|
||||
this.$store.commit("logout", this);
|
||||
this.$router.push({ name: "login" });
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
cancelEditPass() {
|
||||
this.$store.commit("removeTag", "change_password");
|
||||
localStorage.pageOpenedList = JSON.stringify(this.$store.state.app.pageOpenedList);
|
||||
const list = this.$store.state.app.pageOpenedList;
|
||||
const lastPageName = list.length > 1 ? list[list.length - 1].name : list[0].name;
|
||||
this.$router.push({ name: lastPageName });
|
||||
},
|
||||
},
|
||||
defineOptions({ name: "change-password" });
|
||||
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
const userStore = useUserStore();
|
||||
|
||||
const editPasswordFormRef = ref(null);
|
||||
const savePassLoading = ref(false);
|
||||
const editPasswordForm = reactive({
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
rePassword: "",
|
||||
});
|
||||
const strength = ref("");
|
||||
const valideRePassword = (rule, value, callback) => {
|
||||
if (value !== editPasswordForm.newPassword) {
|
||||
callback(new Error("两次输入密码不一致"));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
const passwordValidate = {
|
||||
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
||||
newPassword: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 6, message: "请至少输入6个字符", trigger: "blur" },
|
||||
{ max: 32, message: "最多输入32个字符", trigger: "blur" },
|
||||
],
|
||||
rePassword: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{ validator: valideRePassword, trigger: "blur" },
|
||||
],
|
||||
};
|
||||
|
||||
function changeInputPass(v, grade, strengthVal) {
|
||||
strength.value = strengthVal;
|
||||
}
|
||||
|
||||
function saveEditPass() {
|
||||
const params = {
|
||||
password: md5(editPasswordForm.oldPassword),
|
||||
newPassword: md5(editPasswordForm.newPassword),
|
||||
passStrength: md5(strength.value),
|
||||
};
|
||||
editPasswordFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
savePassLoading.value = true;
|
||||
changePass(params).then((res) => {
|
||||
savePassLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessageBox.alert("修改密码成功,需重新登录", "修改密码成功", {
|
||||
confirmButtonText: "确定",
|
||||
callback: () => {
|
||||
userStore.logout();
|
||||
router.push({ name: "login" });
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function cancelEditPass() {
|
||||
appStore.removeTag("change_password");
|
||||
localStorage.pageOpenedList = JSON.stringify(appStore.pageOpenedList);
|
||||
const list = appStore.pageOpenedList;
|
||||
const lastPageName = list.length > 1 ? list[list.length - 1].name : list[0].name;
|
||||
router.push({ name: lastPageName });
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form ref="formRef" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form-item label="自定义分词" prop="name">
|
||||
<el-input v-model="form.name" clearable style="width: 100%" />
|
||||
</el-form-item>
|
||||
@@ -64,7 +64,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import {
|
||||
getCustomWordsPage,
|
||||
delCustom,
|
||||
@@ -72,160 +73,161 @@ import {
|
||||
updateCustomWords,
|
||||
} from "@/api/index";
|
||||
import { regular } from "@/utils";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
|
||||
export default {
|
||||
name: "custom-words",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
modalType: 0,
|
||||
modalVisible: false,
|
||||
modalTitle: "",
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
words: "",
|
||||
},
|
||||
form: {
|
||||
name: "",
|
||||
},
|
||||
formValidate: {
|
||||
name: [regular.REQUIRED, regular.VARCHAR20],
|
||||
},
|
||||
submitLoading: false,
|
||||
selectList: [],
|
||||
selectCount: 0,
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
this.clearSelectAll();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
clearSelectAll() {
|
||||
this.$refs.table?.clearSelection();
|
||||
},
|
||||
changeSelect(e) {
|
||||
this.selectList = e;
|
||||
this.selectCount = e.length;
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getCustomWordsPage(this.searchForm)
|
||||
.then((res) => {
|
||||
defineOptions({ name: "custom-words" });
|
||||
|
||||
const table = ref(null);
|
||||
const formRef = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const modalType = ref(0);
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref("");
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
words: "",
|
||||
});
|
||||
const form = ref({
|
||||
name: "",
|
||||
});
|
||||
const formValidate = reactive({
|
||||
name: [regular.REQUIRED, regular.VARCHAR20],
|
||||
});
|
||||
const submitLoading = ref(false);
|
||||
const selectList = ref([]);
|
||||
const selectCount = ref(0);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
// 未在 data 声明、detail() 中直接挂到 this 上的字段
|
||||
let id;
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
clearSelectAll();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function clearSelectAll() {
|
||||
table.value?.clearSelection();
|
||||
}
|
||||
function changeSelect(e) {
|
||||
selectList.value = e;
|
||||
selectCount.value = e.length;
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getCustomWordsPage(searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function handleSubmit() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true;
|
||||
if (modalType.value == 0) {
|
||||
delete form.value.id;
|
||||
insertCustomWords(form.value).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.submitLoading = true;
|
||||
if (this.modalType == 0) {
|
||||
delete this.form.id;
|
||||
insertCustomWords(this.form).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.form.id = this.id;
|
||||
updateCustomWords(this.form).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
form.value.id = id;
|
||||
updateCustomWords(form.value).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function add() {
|
||||
modalType.value = 0;
|
||||
modalTitle.value = "添加";
|
||||
form.value = {};
|
||||
formRef.value?.resetFields();
|
||||
modalVisible.value = true;
|
||||
}
|
||||
function detail(v) {
|
||||
modalType.value = 1;
|
||||
id = v.id;
|
||||
modalTitle.value = "修改";
|
||||
modalVisible.value = true;
|
||||
form.value.name = v.name;
|
||||
}
|
||||
function remove(v) {
|
||||
Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除 " + v.name + " ?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
delCustom(v.id).then((res) => {
|
||||
Modal.remove();
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
add() {
|
||||
this.modalType = 0;
|
||||
this.modalTitle = "添加";
|
||||
this.form = {};
|
||||
this.$refs.form?.resetFields();
|
||||
this.modalVisible = true;
|
||||
},
|
||||
detail(v) {
|
||||
this.modalType = 1;
|
||||
this.id = v.id;
|
||||
this.modalTitle = "修改";
|
||||
this.modalVisible = true;
|
||||
this.form.name = v.name;
|
||||
},
|
||||
remove(v) {
|
||||
this.$Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除 " + v.name + " ?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
delCustom(v.id).then((res) => {
|
||||
this.$Modal.remove();
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
function delAll() {
|
||||
if (selectCount.value <= 0) {
|
||||
Message.warning("您还未选择要删除的数据");
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除所选的 " + selectCount.value + " 条数据?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
let ids = "";
|
||||
selectList.value.forEach(function (e) {
|
||||
ids += e.id + ",";
|
||||
});
|
||||
ids = ids.substring(0, ids.length - 1);
|
||||
delCustom(ids).then((res) => {
|
||||
Modal.remove();
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
clearSelectAll();
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
delAll() {
|
||||
if (this.selectCount <= 0) {
|
||||
this.$Message.warning("您还未选择要删除的数据");
|
||||
return;
|
||||
}
|
||||
this.$Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除所选的 " + this.selectCount + " 条数据?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
let ids = "";
|
||||
this.selectList.forEach(function (e) {
|
||||
ids += e.id + ",";
|
||||
});
|
||||
ids = ids.substring(0, ids.length - 1);
|
||||
delCustom(ids).then((res) => {
|
||||
this.$Modal.remove();
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.clearSelectAll();
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="100px"
|
||||
@@ -420,7 +420,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import {
|
||||
auditDistribution,
|
||||
batchUpdateDistributionGrade,
|
||||
@@ -436,469 +438,478 @@ import {
|
||||
updateDistributionMember,
|
||||
} from "@/api/distribution";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
import { unixToDate } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "distribution" });
|
||||
|
||||
const MOBILE_PATTERN = /^1[3-9]\d{9}$/;
|
||||
|
||||
export default {
|
||||
name: "distribution",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
currentDistributionStatus: "ALL",
|
||||
joinTimeRange: [],
|
||||
gradeOptions: [],
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
id: "",
|
||||
mobile: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
nickname: "",
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
detailVisible: false,
|
||||
detailLoading: false,
|
||||
detail: null,
|
||||
editVisible: false,
|
||||
editLoading: false,
|
||||
editForm: {
|
||||
id: "",
|
||||
mobile: "",
|
||||
nickname: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
},
|
||||
editRules: {
|
||||
nickname: [{ required: true, message: "请输入昵称", trigger: "blur" }],
|
||||
gradeId: [{ required: true, message: "请选择分销等级", trigger: "change" }],
|
||||
},
|
||||
createVisible: false,
|
||||
createLoading: false,
|
||||
createForm: {
|
||||
nickname: "",
|
||||
mobile: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
},
|
||||
createRules: {
|
||||
nickname: [{ required: true, message: "请输入昵称", trigger: "blur" }],
|
||||
mobile: [
|
||||
{ required: true, message: "请输入手机号", trigger: "blur" },
|
||||
{ pattern: MOBILE_PATTERN, message: "手机号格式有误", trigger: "blur" },
|
||||
],
|
||||
gradeId: [{ required: true, message: "请选择分销等级", trigger: "change" }],
|
||||
},
|
||||
selectedRows: [],
|
||||
batchGradeVisible: false,
|
||||
batchGradeLoading: false,
|
||||
exportLoading: false,
|
||||
templateLoading: false,
|
||||
importVisible: false,
|
||||
importLoading: false,
|
||||
importFile: null,
|
||||
importFileName: "",
|
||||
batchGradeForm: {
|
||||
gradeId: "",
|
||||
},
|
||||
batchGradeRules: {
|
||||
gradeId: [{ required: true, message: "请选择分销等级", trigger: "change" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadGradeOptions();
|
||||
this.applyRouteQuery();
|
||||
this.getDataList();
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.applyRouteQuery();
|
||||
this.getDataList();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
const editFormRef = ref(null);
|
||||
const createFormRef = ref(null);
|
||||
const batchGradeFormRef = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const currentDistributionStatus = ref("ALL");
|
||||
const joinTimeRange = ref([]);
|
||||
const gradeOptions = ref([]);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
id: "",
|
||||
mobile: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
nickname: "",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const detailVisible = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const detail = ref(null);
|
||||
const editVisible = ref(false);
|
||||
const editLoading = ref(false);
|
||||
const editForm = ref({
|
||||
id: "",
|
||||
mobile: "",
|
||||
nickname: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
});
|
||||
const editRules = {
|
||||
nickname: [{ required: true, message: "请输入昵称", trigger: "blur" }],
|
||||
gradeId: [{ required: true, message: "请选择分销等级", trigger: "change" }],
|
||||
};
|
||||
const createVisible = ref(false);
|
||||
const createLoading = ref(false);
|
||||
const createForm = ref({
|
||||
nickname: "",
|
||||
mobile: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
});
|
||||
const createRules = {
|
||||
nickname: [{ required: true, message: "请输入昵称", trigger: "blur" }],
|
||||
mobile: [
|
||||
{ required: true, message: "请输入手机号", trigger: "blur" },
|
||||
{ pattern: MOBILE_PATTERN, message: "手机号格式有误", trigger: "blur" },
|
||||
],
|
||||
gradeId: [{ required: true, message: "请选择分销等级", trigger: "change" }],
|
||||
};
|
||||
const selectedRows = ref([]);
|
||||
const batchGradeVisible = ref(false);
|
||||
const batchGradeLoading = ref(false);
|
||||
const exportLoading = ref(false);
|
||||
const templateLoading = ref(false);
|
||||
const importVisible = ref(false);
|
||||
const importLoading = ref(false);
|
||||
const importFile = ref(null);
|
||||
const importFileName = ref("");
|
||||
const batchGradeForm = reactive({
|
||||
gradeId: "",
|
||||
});
|
||||
const batchGradeRules = {
|
||||
gradeId: [{ required: true, message: "请选择分销等级", trigger: "change" }],
|
||||
};
|
||||
|
||||
watch(
|
||||
() => route.query,
|
||||
() => {
|
||||
applyRouteQuery();
|
||||
getDataList();
|
||||
}
|
||||
);
|
||||
|
||||
function applyRouteQuery() {
|
||||
const query = route.query || {};
|
||||
searchForm.inviterMobile = query.inviterMobile ? String(query.inviterMobile) : "";
|
||||
}
|
||||
function openCustomerRelationPage() {
|
||||
if (!detail.value) return;
|
||||
const resolved = router.resolve({
|
||||
path: "/distributionCustomerRelation",
|
||||
query: {
|
||||
distributionId: detail.value.id,
|
||||
distributionMobile: detail.value.mobile || "",
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
applyRouteQuery() {
|
||||
const query = this.$route.query || {};
|
||||
this.searchForm.inviterMobile = query.inviterMobile ? String(query.inviterMobile) : "";
|
||||
});
|
||||
window.open(resolved.href, "_blank");
|
||||
}
|
||||
function openInviteeDistributionPage() {
|
||||
if (!detail.value) return;
|
||||
const resolved = router.resolve({
|
||||
path: "/distribution",
|
||||
query: {
|
||||
inviterMobile: detail.value.mobile || "",
|
||||
},
|
||||
openCustomerRelationPage() {
|
||||
if (!this.detail) return;
|
||||
const route = this.$router.resolve({
|
||||
path: "/distributionCustomerRelation",
|
||||
query: {
|
||||
distributionId: this.detail.id,
|
||||
distributionMobile: this.detail.mobile || "",
|
||||
},
|
||||
});
|
||||
window.open(route.href, "_blank");
|
||||
},
|
||||
openInviteeDistributionPage() {
|
||||
if (!this.detail) return;
|
||||
const route = this.$router.resolve({
|
||||
path: "/distribution",
|
||||
query: {
|
||||
inviterMobile: this.detail.mobile || "",
|
||||
},
|
||||
});
|
||||
window.open(route.href, "_blank");
|
||||
},
|
||||
loadGradeOptions() {
|
||||
getDistributionGrades().then((res) => {
|
||||
});
|
||||
window.open(resolved.href, "_blank");
|
||||
}
|
||||
function loadGradeOptions() {
|
||||
getDistributionGrades().then((res) => {
|
||||
if (res.success) {
|
||||
gradeOptions.value = (res.result || []).slice().sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
||||
}
|
||||
});
|
||||
}
|
||||
function statusLabel(status) {
|
||||
const map = {
|
||||
PASS: "审核通过",
|
||||
APPLY: "待审核",
|
||||
RETREAT: "已清退",
|
||||
REFUSE: "审核拒绝",
|
||||
};
|
||||
return map[status] || status || "-";
|
||||
}
|
||||
function formatMoney(value) {
|
||||
const num = Number(value);
|
||||
if (Number.isNaN(num)) return "0.00";
|
||||
return num.toFixed(2);
|
||||
}
|
||||
function onStatusTabChange() {
|
||||
searchForm.pageNumber = 1;
|
||||
clearSelectAll();
|
||||
getDataList();
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
clearSelectAll();
|
||||
getDataList();
|
||||
}
|
||||
function changeSelect(selection) {
|
||||
selectedRows.value = selection || [];
|
||||
}
|
||||
function clearSelectAll() {
|
||||
selectedRows.value = [];
|
||||
table.value?.clearSelection();
|
||||
}
|
||||
function openBatchGrade() {
|
||||
if (!selectedRows.value.length) {
|
||||
Message.warning("请先选择分销员");
|
||||
return;
|
||||
}
|
||||
batchGradeForm.gradeId = "";
|
||||
batchGradeVisible.value = true;
|
||||
}
|
||||
function resetBatchGradeForm() {
|
||||
batchGradeLoading.value = false;
|
||||
batchGradeForm.gradeId = "";
|
||||
batchGradeFormRef.value?.clearValidate();
|
||||
}
|
||||
function submitBatchGrade() {
|
||||
batchGradeFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
batchGradeLoading.value = true;
|
||||
batchUpdateDistributionGrade({
|
||||
ids: selectedRows.value.map((item) => item.id),
|
||||
gradeId: batchGradeForm.gradeId,
|
||||
})
|
||||
.then((res) => {
|
||||
batchGradeLoading.value = false;
|
||||
if (res.success) {
|
||||
this.gradeOptions = (res.result || []).slice().sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
||||
Message.success("设置成功");
|
||||
batchGradeVisible.value = false;
|
||||
clearSelectAll();
|
||||
getDataList();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
batchGradeLoading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
function handleExport() {
|
||||
exportLoading.value = true;
|
||||
const params = buildParams();
|
||||
delete params.pageNumber;
|
||||
delete params.pageSize;
|
||||
exportDistributionMembers(params)
|
||||
.then((blob) => {
|
||||
if (blob && blob.type && blob.type.includes("application/json")) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
try {
|
||||
const result = JSON.parse(reader.result);
|
||||
Message.error(result.message || "导出失败");
|
||||
} catch (e) {
|
||||
Message.error("导出失败");
|
||||
}
|
||||
};
|
||||
reader.readAsText(blob);
|
||||
return;
|
||||
}
|
||||
downloadBlob(blob, `分销员列表_${Date.now()}.xlsx`);
|
||||
Message.success("导出成功");
|
||||
})
|
||||
.catch(() => {
|
||||
Message.error("导出失败");
|
||||
})
|
||||
.finally(() => {
|
||||
exportLoading.value = false;
|
||||
});
|
||||
}
|
||||
function downloadImportTemplate() {
|
||||
templateLoading.value = true;
|
||||
downloadDistributionImportTemplate()
|
||||
.then((blob) => {
|
||||
downloadBlob(blob, "分销员批量导入模板.xlsx");
|
||||
Message.success("模板已下载");
|
||||
})
|
||||
.catch(() => {
|
||||
Message.error("模板下载失败");
|
||||
})
|
||||
.finally(() => {
|
||||
templateLoading.value = false;
|
||||
});
|
||||
}
|
||||
function openImportDialog() {
|
||||
importVisible.value = true;
|
||||
}
|
||||
function resetImportDialog() {
|
||||
importLoading.value = false;
|
||||
importFile.value = null;
|
||||
importFileName.value = "";
|
||||
}
|
||||
function onImportBeforeUpload(file) {
|
||||
importFile.value = file;
|
||||
importFileName.value = file.name;
|
||||
return false;
|
||||
}
|
||||
function submitImport() {
|
||||
if (!importFile.value) {
|
||||
Message.warning("请先选择文件");
|
||||
return;
|
||||
}
|
||||
const fd = new FormData();
|
||||
fd.append("file", importFile.value);
|
||||
importLoading.value = true;
|
||||
importDistributionMembers(fd)
|
||||
.then((res) => {
|
||||
importLoading.value = false;
|
||||
if (res.success) {
|
||||
const result = res.result || {};
|
||||
Message.success(
|
||||
`导入完成:成功 ${result.successCount || 0} 条,失败 ${result.failCount || 0} 条`
|
||||
);
|
||||
importVisible.value = false;
|
||||
getDataList();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
importLoading.value = false;
|
||||
});
|
||||
}
|
||||
function goImportRecord() {
|
||||
router.push({
|
||||
path: "/distributionImportRecord",
|
||||
});
|
||||
}
|
||||
function buildParams() {
|
||||
const params = {
|
||||
pageNumber: searchForm.pageNumber,
|
||||
pageSize: searchForm.pageSize,
|
||||
};
|
||||
if (currentDistributionStatus.value && currentDistributionStatus.value !== "ALL") {
|
||||
params.distributionStatus = currentDistributionStatus.value;
|
||||
}
|
||||
if (searchForm.id) params.id = searchForm.id.trim();
|
||||
if (searchForm.mobile) params.mobile = searchForm.mobile;
|
||||
if (searchForm.inviterMobile) params.inviterMobile = searchForm.inviterMobile;
|
||||
if (searchForm.gradeId) params.gradeId = searchForm.gradeId;
|
||||
if (searchForm.nickname) params.nickname = searchForm.nickname;
|
||||
if (joinTimeRange.value && joinTimeRange.value[0] && joinTimeRange.value[1]) {
|
||||
const startTime = new Date(joinTimeRange.value[0]).getTime();
|
||||
const endTime = new Date(joinTimeRange.value[1]).getTime();
|
||||
params.joinTimeStart = unixToDate(startTime / 1000);
|
||||
params.joinTimeEnd = unixToDate(endTime / 1000);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getDistributionMemberPage(buildParams())
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function openDetail(row) {
|
||||
detailVisible.value = true;
|
||||
detailLoading.value = true;
|
||||
detail.value = null;
|
||||
getDistributionMemberDetail(row.id)
|
||||
.then((res) => {
|
||||
detailLoading.value = false;
|
||||
if (res.success) {
|
||||
detail.value = res.result;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
detailLoading.value = false;
|
||||
});
|
||||
}
|
||||
function openCreate() {
|
||||
createForm.value = {
|
||||
nickname: "",
|
||||
mobile: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
};
|
||||
createVisible.value = true;
|
||||
}
|
||||
function resetCreateForm() {
|
||||
createLoading.value = false;
|
||||
createForm.value = {
|
||||
nickname: "",
|
||||
mobile: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
};
|
||||
createFormRef.value?.clearValidate();
|
||||
}
|
||||
function submitCreate() {
|
||||
createFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
createLoading.value = true;
|
||||
createDistributionMember({
|
||||
nickname: createForm.value.nickname,
|
||||
mobile: createForm.value.mobile,
|
||||
inviterMobile: createForm.value.inviterMobile || "",
|
||||
gradeId: createForm.value.gradeId,
|
||||
})
|
||||
.then((res) => {
|
||||
createLoading.value = false;
|
||||
if (res.success) {
|
||||
Message.success("新增成功");
|
||||
createVisible.value = false;
|
||||
getDataList();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
createLoading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
function openEdit(row) {
|
||||
editForm.value = {
|
||||
id: row.id,
|
||||
mobile: row.mobile || "",
|
||||
nickname: row.nickname || "",
|
||||
inviterMobile: row.inviterMobile || "",
|
||||
gradeId: resolveGradeOptionId(row.gradeId),
|
||||
};
|
||||
editVisible.value = true;
|
||||
}
|
||||
function resolveGradeOptionId(gradeRef) {
|
||||
if (!gradeRef || !gradeOptions.value.length) return gradeRef || "";
|
||||
const direct = gradeOptions.value.find((item) => item.id === gradeRef);
|
||||
if (direct) return direct.id;
|
||||
const bySort = gradeOptions.value.find((item) => String(item.sortOrder) === String(gradeRef));
|
||||
return bySort ? bySort.id : gradeRef;
|
||||
}
|
||||
function resetEditForm() {
|
||||
editLoading.value = false;
|
||||
editForm.value = {
|
||||
id: "",
|
||||
mobile: "",
|
||||
nickname: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
};
|
||||
editFormRef.value?.clearValidate();
|
||||
}
|
||||
function submitEdit() {
|
||||
editFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
editLoading.value = true;
|
||||
updateDistributionMember(editForm.value.id, {
|
||||
nickname: editForm.value.nickname,
|
||||
inviterMobile: editForm.value.inviterMobile || "",
|
||||
gradeId: editForm.value.gradeId,
|
||||
})
|
||||
.then((res) => {
|
||||
editLoading.value = false;
|
||||
if (res.success) {
|
||||
Message.success("保存成功");
|
||||
editVisible.value = false;
|
||||
getDataList();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
editLoading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
function audit(row, status) {
|
||||
const text = status === "PASS" ? "通过" : "拒绝";
|
||||
Modal.confirm({
|
||||
title: "审核确认",
|
||||
content: `确认${text}该分销员申请?`,
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
auditDistribution(row.id, { status }).then((res) => {
|
||||
Modal.remove();
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
statusLabel(status) {
|
||||
const map = {
|
||||
PASS: "审核通过",
|
||||
APPLY: "待审核",
|
||||
RETREAT: "已清退",
|
||||
REFUSE: "审核拒绝",
|
||||
};
|
||||
return map[status] || status || "-";
|
||||
},
|
||||
formatMoney(value) {
|
||||
const num = Number(value);
|
||||
if (Number.isNaN(num)) return "0.00";
|
||||
return num.toFixed(2);
|
||||
},
|
||||
onStatusTabChange() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.clearSelectAll();
|
||||
this.getDataList();
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.clearSelectAll();
|
||||
this.getDataList();
|
||||
},
|
||||
changeSelect(selection) {
|
||||
this.selectedRows = selection || [];
|
||||
},
|
||||
clearSelectAll() {
|
||||
this.selectedRows = [];
|
||||
this.$refs.table?.clearSelection();
|
||||
},
|
||||
openBatchGrade() {
|
||||
if (!this.selectedRows.length) {
|
||||
this.$Message.warning("请先选择分销员");
|
||||
return;
|
||||
}
|
||||
this.batchGradeForm.gradeId = "";
|
||||
this.batchGradeVisible = true;
|
||||
},
|
||||
resetBatchGradeForm() {
|
||||
this.batchGradeLoading = false;
|
||||
this.batchGradeForm.gradeId = "";
|
||||
this.$refs.batchGradeFormRef?.clearValidate();
|
||||
},
|
||||
submitBatchGrade() {
|
||||
this.$refs.batchGradeFormRef.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.batchGradeLoading = true;
|
||||
batchUpdateDistributionGrade({
|
||||
ids: this.selectedRows.map((item) => item.id),
|
||||
gradeId: this.batchGradeForm.gradeId,
|
||||
})
|
||||
.then((res) => {
|
||||
this.batchGradeLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("设置成功");
|
||||
this.batchGradeVisible = false;
|
||||
this.clearSelectAll();
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.batchGradeLoading = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
function retreat(row) {
|
||||
Modal.confirm({
|
||||
title: "清退确认",
|
||||
content: "确认清退该分销员?清退后将停止后续计佣。",
|
||||
onOk: () => {
|
||||
retreatDistribution(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
this.exportLoading = true;
|
||||
const params = this.buildParams();
|
||||
delete params.pageNumber;
|
||||
delete params.pageSize;
|
||||
exportDistributionMembers(params)
|
||||
.then((blob) => {
|
||||
if (blob && blob.type && blob.type.includes("application/json")) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
try {
|
||||
const result = JSON.parse(reader.result);
|
||||
this.$Message.error(result.message || "导出失败");
|
||||
} catch (e) {
|
||||
this.$Message.error("导出失败");
|
||||
}
|
||||
};
|
||||
reader.readAsText(blob);
|
||||
return;
|
||||
}
|
||||
downloadBlob(blob, `分销员列表_${Date.now()}.xlsx`);
|
||||
this.$Message.success("导出成功");
|
||||
})
|
||||
.catch(() => {
|
||||
this.$Message.error("导出失败");
|
||||
})
|
||||
.finally(() => {
|
||||
this.exportLoading = false;
|
||||
});
|
||||
},
|
||||
downloadImportTemplate() {
|
||||
this.templateLoading = true;
|
||||
downloadDistributionImportTemplate()
|
||||
.then((blob) => {
|
||||
downloadBlob(blob, "分销员批量导入模板.xlsx");
|
||||
this.$Message.success("模板已下载");
|
||||
})
|
||||
.catch(() => {
|
||||
this.$Message.error("模板下载失败");
|
||||
})
|
||||
.finally(() => {
|
||||
this.templateLoading = false;
|
||||
});
|
||||
},
|
||||
openImportDialog() {
|
||||
this.importVisible = true;
|
||||
},
|
||||
resetImportDialog() {
|
||||
this.importLoading = false;
|
||||
this.importFile = null;
|
||||
this.importFileName = "";
|
||||
},
|
||||
onImportBeforeUpload(file) {
|
||||
this.importFile = file;
|
||||
this.importFileName = file.name;
|
||||
return false;
|
||||
},
|
||||
submitImport() {
|
||||
if (!this.importFile) {
|
||||
this.$Message.warning("请先选择文件");
|
||||
return;
|
||||
}
|
||||
const fd = new FormData();
|
||||
fd.append("file", this.importFile);
|
||||
this.importLoading = true;
|
||||
importDistributionMembers(fd)
|
||||
.then((res) => {
|
||||
this.importLoading = false;
|
||||
if (res.success) {
|
||||
const result = res.result || {};
|
||||
this.$Message.success(
|
||||
`导入完成:成功 ${result.successCount || 0} 条,失败 ${result.failCount || 0} 条`
|
||||
);
|
||||
this.importVisible = false;
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.importLoading = false;
|
||||
});
|
||||
},
|
||||
goImportRecord() {
|
||||
this.$router.push({
|
||||
path: "/distributionImportRecord",
|
||||
});
|
||||
}
|
||||
function resume(row) {
|
||||
Modal.confirm({
|
||||
title: "恢复确认",
|
||||
content: "确认恢复该分销员?",
|
||||
onOk: () => {
|
||||
resumeDistribution(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
buildParams() {
|
||||
const params = {
|
||||
pageNumber: this.searchForm.pageNumber,
|
||||
pageSize: this.searchForm.pageSize,
|
||||
};
|
||||
if (this.currentDistributionStatus && this.currentDistributionStatus !== "ALL") {
|
||||
params.distributionStatus = this.currentDistributionStatus;
|
||||
}
|
||||
if (this.searchForm.id) params.id = this.searchForm.id.trim();
|
||||
if (this.searchForm.mobile) params.mobile = this.searchForm.mobile;
|
||||
if (this.searchForm.inviterMobile) params.inviterMobile = this.searchForm.inviterMobile;
|
||||
if (this.searchForm.gradeId) params.gradeId = this.searchForm.gradeId;
|
||||
if (this.searchForm.nickname) params.nickname = this.searchForm.nickname;
|
||||
if (this.joinTimeRange && this.joinTimeRange[0] && this.joinTimeRange[1]) {
|
||||
const startTime = new Date(this.joinTimeRange[0]).getTime();
|
||||
const endTime = new Date(this.joinTimeRange[1]).getTime();
|
||||
params.joinTimeStart = this.$filters.unixToDate(startTime / 1000);
|
||||
params.joinTimeEnd = this.$filters.unixToDate(endTime / 1000);
|
||||
}
|
||||
return params;
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getDistributionMemberPage(this.buildParams())
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
openDetail(row) {
|
||||
this.detailVisible = true;
|
||||
this.detailLoading = true;
|
||||
this.detail = null;
|
||||
getDistributionMemberDetail(row.id)
|
||||
.then((res) => {
|
||||
this.detailLoading = false;
|
||||
if (res.success) {
|
||||
this.detail = res.result;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
openCreate() {
|
||||
this.createForm = {
|
||||
nickname: "",
|
||||
mobile: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
};
|
||||
this.createVisible = true;
|
||||
},
|
||||
resetCreateForm() {
|
||||
this.createLoading = false;
|
||||
this.createForm = {
|
||||
nickname: "",
|
||||
mobile: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
};
|
||||
this.$refs.createFormRef?.clearValidate();
|
||||
},
|
||||
submitCreate() {
|
||||
this.$refs.createFormRef.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.createLoading = true;
|
||||
createDistributionMember({
|
||||
nickname: this.createForm.nickname,
|
||||
mobile: this.createForm.mobile,
|
||||
inviterMobile: this.createForm.inviterMobile || "",
|
||||
gradeId: this.createForm.gradeId,
|
||||
})
|
||||
.then((res) => {
|
||||
this.createLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("新增成功");
|
||||
this.createVisible = false;
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.createLoading = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
openEdit(row) {
|
||||
this.editForm = {
|
||||
id: row.id,
|
||||
mobile: row.mobile || "",
|
||||
nickname: row.nickname || "",
|
||||
inviterMobile: row.inviterMobile || "",
|
||||
gradeId: this.resolveGradeOptionId(row.gradeId),
|
||||
};
|
||||
this.editVisible = true;
|
||||
},
|
||||
resolveGradeOptionId(gradeRef) {
|
||||
if (!gradeRef || !this.gradeOptions.length) return gradeRef || "";
|
||||
const direct = this.gradeOptions.find((item) => item.id === gradeRef);
|
||||
if (direct) return direct.id;
|
||||
const bySort = this.gradeOptions.find((item) => String(item.sortOrder) === String(gradeRef));
|
||||
return bySort ? bySort.id : gradeRef;
|
||||
},
|
||||
resetEditForm() {
|
||||
this.editLoading = false;
|
||||
this.editForm = {
|
||||
id: "",
|
||||
mobile: "",
|
||||
nickname: "",
|
||||
inviterMobile: "",
|
||||
gradeId: "",
|
||||
};
|
||||
this.$refs.editFormRef?.clearValidate();
|
||||
},
|
||||
submitEdit() {
|
||||
this.$refs.editFormRef.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.editLoading = true;
|
||||
updateDistributionMember(this.editForm.id, {
|
||||
nickname: this.editForm.nickname,
|
||||
inviterMobile: this.editForm.inviterMobile || "",
|
||||
gradeId: this.editForm.gradeId,
|
||||
})
|
||||
.then((res) => {
|
||||
this.editLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("保存成功");
|
||||
this.editVisible = false;
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.editLoading = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
audit(row, status) {
|
||||
const text = status === "PASS" ? "通过" : "拒绝";
|
||||
this.$Modal.confirm({
|
||||
title: "审核确认",
|
||||
content: `确认${text}该分销员申请?`,
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
auditDistribution(row.id, { status }).then((res) => {
|
||||
this.$Modal.remove();
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
retreat(row) {
|
||||
this.$Modal.confirm({
|
||||
title: "清退确认",
|
||||
content: "确认清退该分销员?清退后将停止后续计佣。",
|
||||
onOk: () => {
|
||||
retreatDistribution(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
resume(row) {
|
||||
this.$Modal.confirm({
|
||||
title: "恢复确认",
|
||||
content: "确认恢复该分销员?",
|
||||
onOk: () => {
|
||||
resumeDistribution(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadGradeOptions();
|
||||
applyRouteQuery();
|
||||
getDataList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
class="search-form"
|
||||
@keyup.enter="handleSearch"
|
||||
@@ -173,144 +173,147 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { auditDistributionCash, getDistributionCash } from "@/api/distribution";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
|
||||
export default {
|
||||
name: "distributionCash",
|
||||
data() {
|
||||
return {
|
||||
tabStatus: "",
|
||||
applyCount: 0,
|
||||
loading: true,
|
||||
drawerVisible: false,
|
||||
drawerTitle: "",
|
||||
result: "VIA_AUDITING",
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
},
|
||||
handleStatus: "edit",
|
||||
form: {
|
||||
sn: "",
|
||||
distributionId: "",
|
||||
distributionName: "",
|
||||
price: "",
|
||||
name: "",
|
||||
settlementBankAccountName: "",
|
||||
settlementBankAccountNum: "",
|
||||
},
|
||||
submitLoading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
applyCountLabel() {
|
||||
return this.applyCount > 0 ? `待处理(${this.applyCount})` : "待处理";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getDataList();
|
||||
this.loadApplyCount();
|
||||
},
|
||||
loadApplyCount() {
|
||||
getDistributionCash({
|
||||
distributionCashStatus: "APPLY",
|
||||
pageSize: 1,
|
||||
pageNumber: 1,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
this.applyCount = res.result.total || 0;
|
||||
}
|
||||
});
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
const params = { ...this.searchForm };
|
||||
if (this.tabStatus) {
|
||||
params.distributionCashStatus = this.tabStatus;
|
||||
}
|
||||
getDistributionCash(params).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleTabChange(name) {
|
||||
this.tabStatus = name;
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSubmit() {
|
||||
let result = this.result == "VIA_AUDITING" ? "通过" : "拒绝";
|
||||
this.$Modal.confirm({
|
||||
title: "确认审核",
|
||||
content: "您确认要审核" + result + "么?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
auditDistributionCash(this.form.id, { result: this.result }).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Modal.remove();
|
||||
this.$Message.success("审核成功");
|
||||
this.drawerVisible = false;
|
||||
this.getDataList();
|
||||
this.loadApplyCount();
|
||||
} else {
|
||||
this.$Modal.remove();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
edit(v) {
|
||||
this.drawerTitle = "审核";
|
||||
this.handleStatus = "edit";
|
||||
this.result = "VIA_AUDITING";
|
||||
const data = {};
|
||||
for (let attr in v) {
|
||||
data[attr] = v[attr] === null ? "" : v[attr];
|
||||
}
|
||||
this.form = data;
|
||||
this.drawerVisible = true;
|
||||
},
|
||||
view(v) {
|
||||
this.drawerTitle = "查看";
|
||||
this.handleStatus = "view";
|
||||
const data = {};
|
||||
for (let attr in v) {
|
||||
data[attr] = v[attr] === null ? "" : v[attr];
|
||||
}
|
||||
this.form = data;
|
||||
this.drawerVisible = true;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// 处理路由参数,自动跳转到指定标签页
|
||||
const tabStatus = this.$route.query.tabStatus;
|
||||
if (tabStatus) {
|
||||
this.tabStatus = tabStatus;
|
||||
defineOptions({ name: "distributionCash" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const tabStatus = ref("");
|
||||
const applyCount = ref(0);
|
||||
const loading = ref(true);
|
||||
const drawerVisible = ref(false);
|
||||
const drawerTitle = ref("");
|
||||
const result = ref("VIA_AUDITING");
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
});
|
||||
const handleStatus = ref("edit");
|
||||
const form = ref({
|
||||
sn: "",
|
||||
distributionId: "",
|
||||
distributionName: "",
|
||||
price: "",
|
||||
name: "",
|
||||
settlementBankAccountName: "",
|
||||
settlementBankAccountNum: "",
|
||||
});
|
||||
const submitLoading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
const applyCountLabel = computed(() => {
|
||||
return applyCount.value > 0 ? `待处理(${applyCount.value})` : "待处理";
|
||||
});
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
loadApplyCount();
|
||||
}
|
||||
function loadApplyCount() {
|
||||
getDistributionCash({
|
||||
distributionCashStatus: "APPLY",
|
||||
pageSize: 1,
|
||||
pageNumber: 1,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
applyCount.value = res.result.total || 0;
|
||||
}
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.value.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
const params = { ...searchForm.value };
|
||||
if (tabStatus.value) {
|
||||
params.distributionCashStatus = tabStatus.value;
|
||||
}
|
||||
getDistributionCash(params).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleTabChange(name) {
|
||||
tabStatus.value = name;
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleSubmit() {
|
||||
let resultText = result.value == "VIA_AUDITING" ? "通过" : "拒绝";
|
||||
Modal.confirm({
|
||||
title: "确认审核",
|
||||
content: "您确认要审核" + resultText + "么?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
auditDistributionCash(form.value.id, { result: result.value }).then((res) => {
|
||||
if (res.success) {
|
||||
Modal.remove();
|
||||
Message.success("审核成功");
|
||||
drawerVisible.value = false;
|
||||
getDataList();
|
||||
loadApplyCount();
|
||||
} else {
|
||||
Modal.remove();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
function edit(v) {
|
||||
drawerTitle.value = "审核";
|
||||
handleStatus.value = "edit";
|
||||
result.value = "VIA_AUDITING";
|
||||
const formData = {};
|
||||
for (let attr in v) {
|
||||
formData[attr] = v[attr] === null ? "" : v[attr];
|
||||
}
|
||||
form.value = formData;
|
||||
drawerVisible.value = true;
|
||||
}
|
||||
function view(v) {
|
||||
drawerTitle.value = "查看";
|
||||
handleStatus.value = "view";
|
||||
const formData = {};
|
||||
for (let attr in v) {
|
||||
formData[attr] = v[attr] === null ? "" : v[attr];
|
||||
}
|
||||
form.value = formData;
|
||||
drawerVisible.value = true;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 处理路由参数,自动跳转到指定标签页
|
||||
const queryTabStatus = route.query.tabStatus;
|
||||
if (queryTabStatus) {
|
||||
tabStatus.value = queryTabStatus;
|
||||
}
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ops a {
|
||||
|
||||
359
manager/src/views/distribution/distributionCustomer.vue
Normal file
359
manager/src/views/distribution/distributionCustomer.vue
Normal file
@@ -0,0 +1,359 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-tabs v-model="activeTab" @tab-change="handleTabChange">
|
||||
<el-tab-pane label="客户绑定" name="customer">
|
||||
<el-card>
|
||||
<el-form ref="searchFormRef" :model="searchForm" inline class="search-form" @keyup.enter="handleSearch">
|
||||
<el-form-item label="客户昵称">
|
||||
<el-input v-model="searchForm.memberName" clearable placeholder="客户昵称" style="width: 180px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号">
|
||||
<el-input v-model="searchForm.memberMobile" clearable placeholder="手机号" style="width: 180px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分销员">
|
||||
<el-input v-model="searchForm.distributionName" clearable placeholder="分销员名称" style="width: 180px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="searchForm.status" clearable style="width: 120px">
|
||||
<el-option v-for="item in bindStatusList" :key="item.value" :value="item.value" :label="item.label" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="来源">
|
||||
<el-select v-model="searchForm.bindSource" clearable style="width: 140px">
|
||||
<el-option v-for="item in bindSourceList" :key="item.value" :value="item.value" :label="item.label" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleSearch">搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<el-table v-loading="loading" border :data="data" class="mt_10">
|
||||
<el-table-column prop="memberName" label="客户昵称" min-width="120" />
|
||||
<el-table-column prop="memberMobile" label="手机号" min-width="120" />
|
||||
<el-table-column prop="distributionName" label="归属分销员" min-width="120" />
|
||||
<el-table-column label="绑定来源" min-width="100">
|
||||
<template #default="{ row }">{{ bindSourceLabel(row.bindSource) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="绑定类型" min-width="90">
|
||||
<template #default="{ row }">
|
||||
{{ row.bindType === 'PERMANENT' ? '永久' : '短期' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="bindStartTime" label="绑定时间" min-width="160" />
|
||||
<el-table-column prop="bindExpireTime" label="到期时间" min-width="160" />
|
||||
<el-table-column label="保护期剩余" min-width="100">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.protectRemainDays != null">{{ row.protectRemainDays }} 天</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="90">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.status === 'ACTIVE'" type="success">有效</el-tag>
|
||||
<el-tag v-else type="info">已过期</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="160" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
<a class="link-text" @click="openRebind(row)">换绑</a>
|
||||
<span class="split">|</span>
|
||||
<a class="link-text" @click="viewMemberLog(row)">日志</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="mt_10 page padding-row" 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"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@current-change="getDataList"
|
||||
@size-change="handlePageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="绑定日志" name="log">
|
||||
<el-card>
|
||||
<el-form ref="logSearchFormRef" :model="logSearchForm" inline class="search-form" @keyup.enter="handleLogSearch">
|
||||
<el-form-item label="客户昵称">
|
||||
<el-input v-model="logSearchForm.memberName" clearable placeholder="客户昵称" style="width: 180px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="变更类型">
|
||||
<el-select v-model="logSearchForm.changeType" clearable style="width: 140px">
|
||||
<el-option v-for="item in bindChangeTypeList" :key="item.value" :value="item.value" :label="item.label" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="时间">
|
||||
<el-date-picker
|
||||
v-model="logDateRange"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
start-placeholder="开始"
|
||||
end-placeholder="结束"
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleLogSearch">搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<el-table v-loading="logLoading" border :data="logData" class="mt_10">
|
||||
<el-table-column prop="createTime" label="时间" min-width="160" />
|
||||
<el-table-column prop="memberName" label="客户" min-width="100" />
|
||||
<el-table-column prop="fromDistributionName" label="原分销员" min-width="110" />
|
||||
<el-table-column prop="toDistributionName" label="新分销员" min-width="110" />
|
||||
<el-table-column label="变更类型" min-width="100">
|
||||
<template #default="{ row }">{{ bindChangeTypeLabel(row.changeType) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="operatorType" label="操作方" width="90" />
|
||||
<el-table-column prop="remark" label="备注" min-width="160" show-overflow-tooltip />
|
||||
</el-table>
|
||||
<div class="mt_10 page padding-row" style="display: flex; justify-content: flex-end">
|
||||
<el-pagination
|
||||
v-model:current-page="logSearchForm.pageNumber"
|
||||
v-model:page-size="logSearchForm.pageSize"
|
||||
:page-sizes="[20, 50, 100]"
|
||||
:total="logTotal"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@current-change="getLogList"
|
||||
@size-change="handleLogPageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-dialog v-model="rebindVisible" title="手动换绑" width="520px">
|
||||
<el-form ref="rebindFormRef" :model="rebindForm" :rules="rebindRules" label-width="100px">
|
||||
<el-form-item label="客户">
|
||||
<el-input :model-value="rebindForm.memberLabel" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="新分销员" prop="distributionId">
|
||||
<el-select
|
||||
v-model="rebindForm.distributionId"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="输入分销员名称搜索"
|
||||
:remote-method="searchDistributor"
|
||||
:loading="distributorLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in distributorOptions"
|
||||
:key="item.id"
|
||||
:label="item.memberName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注">
|
||||
<el-input v-model="rebindForm.remark" type="textarea" :rows="3" maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="rebindVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="rebindSubmitting" @click="submitRebind">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import {
|
||||
getDistributionCustomerPage,
|
||||
getDistributionBindLogPage,
|
||||
getDistributionListData,
|
||||
rebindDistributionCustomer,
|
||||
} from "@/api/distribution";
|
||||
import { bindStatusList, bindSourceList, bindChangeTypeList } from "./dataJson";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
defineOptions({ name: "distributionCustomer" });
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm"/"logSearchForm",与 data 同名会互相覆盖,改名为 searchFormRef/logSearchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const logSearchFormRef = ref(null);
|
||||
const rebindFormRef = ref(null);
|
||||
|
||||
const activeTab = ref("customer");
|
||||
const loading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "bindStartTime",
|
||||
order: "desc",
|
||||
memberName: "",
|
||||
memberMobile: "",
|
||||
distributionName: "",
|
||||
status: "",
|
||||
bindSource: "",
|
||||
});
|
||||
const logLoading = ref(false);
|
||||
const logData = ref([]);
|
||||
const logTotal = ref(0);
|
||||
const logDateRange = ref([]);
|
||||
const logSearchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
memberName: "",
|
||||
memberId: "",
|
||||
changeType: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
});
|
||||
const rebindVisible = ref(false);
|
||||
const rebindSubmitting = ref(false);
|
||||
const distributorLoading = ref(false);
|
||||
const distributorOptions = ref([]);
|
||||
const rebindForm = ref({
|
||||
memberId: "",
|
||||
memberLabel: "",
|
||||
distributionId: "",
|
||||
remark: "",
|
||||
});
|
||||
const rebindRules = {
|
||||
distributionId: [{ required: true, message: "请选择分销员", trigger: "change" }],
|
||||
};
|
||||
|
||||
function bindSourceLabel(value) {
|
||||
const item = bindSourceList.find((i) => i.value === value);
|
||||
return item ? item.label : value;
|
||||
}
|
||||
function bindChangeTypeLabel(value) {
|
||||
const item = bindChangeTypeList.find((i) => i.value === value);
|
||||
return item ? item.label : value;
|
||||
}
|
||||
function handleTabChange(name) {
|
||||
if (name === "log" && logData.value.length === 0) {
|
||||
getLogList();
|
||||
}
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handlePageSizeChange() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getDistributionCustomerPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleLogSearch() {
|
||||
logSearchForm.pageNumber = 1;
|
||||
if (logDateRange.value && logDateRange.value.length === 2) {
|
||||
logSearchForm.startDate = logDateRange.value[0];
|
||||
logSearchForm.endDate = logDateRange.value[1];
|
||||
} else {
|
||||
logSearchForm.startDate = "";
|
||||
logSearchForm.endDate = "";
|
||||
}
|
||||
getLogList();
|
||||
}
|
||||
function handleLogPageSizeChange() {
|
||||
logSearchForm.pageNumber = 1;
|
||||
getLogList();
|
||||
}
|
||||
function getLogList() {
|
||||
logLoading.value = true;
|
||||
getDistributionBindLogPage(logSearchForm).then((res) => {
|
||||
logLoading.value = false;
|
||||
if (res.success) {
|
||||
logData.value = res.result.records;
|
||||
logTotal.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function openRebind(row) {
|
||||
rebindForm.value = {
|
||||
memberId: row.memberId,
|
||||
memberLabel: `${row.memberName || ""}${row.memberMobile ? " / " + row.memberMobile : ""}`,
|
||||
distributionId: "",
|
||||
remark: "",
|
||||
};
|
||||
distributorOptions.value = [];
|
||||
rebindVisible.value = true;
|
||||
}
|
||||
function searchDistributor(query) {
|
||||
if (!query) {
|
||||
distributorOptions.value = [];
|
||||
return;
|
||||
}
|
||||
distributorLoading.value = true;
|
||||
getDistributionListData({
|
||||
memberName: query,
|
||||
distributionStatus: "PASS",
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
}).then((res) => {
|
||||
distributorLoading.value = false;
|
||||
if (res.success) {
|
||||
distributorOptions.value = (res.result.records || []).filter((item) => item.id !== rebindForm.value.distributionId);
|
||||
}
|
||||
});
|
||||
}
|
||||
function submitRebind() {
|
||||
rebindFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
rebindSubmitting.value = true;
|
||||
rebindDistributionCustomer({
|
||||
memberId: rebindForm.value.memberId,
|
||||
distributionId: rebindForm.value.distributionId,
|
||||
remark: rebindForm.value.remark,
|
||||
}).then((res) => {
|
||||
rebindSubmitting.value = false;
|
||||
if (res.success) {
|
||||
Message.success("换绑成功");
|
||||
rebindVisible.value = false;
|
||||
getDataList();
|
||||
}
|
||||
}).catch(() => {
|
||||
rebindSubmitting.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
function viewMemberLog(row) {
|
||||
activeTab.value = "log";
|
||||
logSearchForm.memberId = row.memberId;
|
||||
logSearchForm.memberName = "";
|
||||
logSearchForm.pageNumber = 1;
|
||||
getLogList();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.link-text {
|
||||
color: #2d8cf0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.split {
|
||||
margin: 0 8px;
|
||||
color: #dcdee2;
|
||||
}
|
||||
</style>
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="110px"
|
||||
@@ -250,7 +250,7 @@
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="rebindForm" :model="rebindForm" :rules="rebindRules" label-width="110px">
|
||||
<el-form ref="rebindFormRef" :model="rebindForm" :rules="rebindRules" label-width="110px">
|
||||
<el-form-item label="客户">
|
||||
<span>{{ rebindForm.memberNickname || rebindForm.memberMobile || rebindForm.memberId }}</span>
|
||||
</el-form-item>
|
||||
@@ -288,7 +288,7 @@
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="unbindForm" :model="unbindForm" label-width="110px">
|
||||
<el-form ref="unbindFormRef" :model="unbindForm" label-width="110px">
|
||||
<el-form-item label="客户">
|
||||
<span>{{ unbindForm.memberNickname || unbindForm.memberMobile || unbindForm.memberId }}</span>
|
||||
</el-form-item>
|
||||
@@ -304,7 +304,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import {
|
||||
getCustomerRelationDetail,
|
||||
getCustomerRelationLogs,
|
||||
@@ -315,259 +317,260 @@ import {
|
||||
import { relationEffectiveList, hasDeadlineList } from "./dataJson.js";
|
||||
import DistributionSelector from "@/components/distribution/distribution-selector.vue";
|
||||
import { QuestionFilled } from "@element-plus/icons-vue";
|
||||
import { Message } from "@/utils/message";
|
||||
import { unixToDate } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "distributionCustomerRelation",
|
||||
components: { QuestionFilled, DistributionSelector },
|
||||
data() {
|
||||
return {
|
||||
relationEffectiveList,
|
||||
hasDeadlineList,
|
||||
bindTimeRange: [],
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
memberMobile: "",
|
||||
distributionMobile: "",
|
||||
distributionId: "",
|
||||
relationEffective: "",
|
||||
hasDeadline: "",
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
detailVisible: false,
|
||||
detailLoading: false,
|
||||
detail: null,
|
||||
logVisible: false,
|
||||
logLoading: false,
|
||||
logList: [],
|
||||
logTarget: null,
|
||||
rebindVisible: false,
|
||||
distributorSelectorVisible: false,
|
||||
unbindVisible: false,
|
||||
rebindLoading: false,
|
||||
unbindLoading: false,
|
||||
rebindForm: {
|
||||
memberId: "",
|
||||
memberNickname: "",
|
||||
memberMobile: "",
|
||||
currentDistributionId: "",
|
||||
distributionId: "",
|
||||
distributionNickname: "",
|
||||
distributionMobile: "",
|
||||
reason: "",
|
||||
},
|
||||
unbindForm: {
|
||||
memberId: "",
|
||||
memberNickname: "",
|
||||
memberMobile: "",
|
||||
reason: "",
|
||||
},
|
||||
rebindRules: {
|
||||
distributionId: [{
|
||||
required: true,
|
||||
message: "请选择分销员",
|
||||
trigger: "change",
|
||||
}],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selectedDistributorLabel() {
|
||||
if (!this.rebindForm.distributionId) return "";
|
||||
const name = this.rebindForm.distributionNickname || "-";
|
||||
const mobile = this.rebindForm.distributionMobile || "-";
|
||||
return `${name}(${mobile})`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.applyRouteQuery();
|
||||
this.getDataList();
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.applyRouteQuery();
|
||||
this.getDataList();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
applyRouteQuery() {
|
||||
const query = this.$route.query || {};
|
||||
this.searchForm.distributionId = query.distributionId ? String(query.distributionId) : "";
|
||||
this.searchForm.distributionMobile = query.distributionMobile ? String(query.distributionMobile) : "";
|
||||
},
|
||||
formatInvalidTime(row) {
|
||||
return row?.invalidTime || "-";
|
||||
},
|
||||
formatDetailInvalidTime(detail) {
|
||||
if (!detail || this.isRelationEffective(detail)) return "-";
|
||||
return detail.invalidTime || "-";
|
||||
},
|
||||
formatDetailInvalidReason(detail) {
|
||||
if (!detail || this.isRelationEffective(detail)) return "-";
|
||||
return detail.invalidReason || "客户关系失效";
|
||||
},
|
||||
isRelationEffective(row) {
|
||||
if (!row) return false;
|
||||
if (row.relationEffective === true) return true;
|
||||
if (row.relationEffective === false) return false;
|
||||
return !["EXPIRED", "UNBOUND"].includes(row.relationStatus);
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
const params = { ...this.searchForm };
|
||||
if (!params.memberMobile) delete params.memberMobile;
|
||||
if (!params.distributionMobile) delete params.distributionMobile;
|
||||
if (!params.distributionId) delete params.distributionId;
|
||||
if (params.relationEffective === "" || params.relationEffective === null || params.relationEffective === undefined) {
|
||||
delete params.relationEffective;
|
||||
} else {
|
||||
params.relationEffective = params.relationEffective === true || params.relationEffective === "true";
|
||||
defineOptions({ name: "distributionCustomerRelation" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
const rebindFormRef = ref(null);
|
||||
const unbindFormRef = ref(null);
|
||||
|
||||
const bindTimeRange = ref([]);
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
memberMobile: "",
|
||||
distributionMobile: "",
|
||||
distributionId: "",
|
||||
relationEffective: "",
|
||||
hasDeadline: "",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const detailVisible = ref(false);
|
||||
const detailLoading = ref(false);
|
||||
const detail = ref(null);
|
||||
const logVisible = ref(false);
|
||||
const logLoading = ref(false);
|
||||
const logList = ref([]);
|
||||
const logTarget = ref(null);
|
||||
const rebindVisible = ref(false);
|
||||
const distributorSelectorVisible = ref(false);
|
||||
const unbindVisible = ref(false);
|
||||
const rebindLoading = ref(false);
|
||||
const unbindLoading = ref(false);
|
||||
const rebindForm = ref({
|
||||
memberId: "",
|
||||
memberNickname: "",
|
||||
memberMobile: "",
|
||||
currentDistributionId: "",
|
||||
distributionId: "",
|
||||
distributionNickname: "",
|
||||
distributionMobile: "",
|
||||
reason: "",
|
||||
});
|
||||
const unbindForm = ref({
|
||||
memberId: "",
|
||||
memberNickname: "",
|
||||
memberMobile: "",
|
||||
reason: "",
|
||||
});
|
||||
const rebindRules = reactive({
|
||||
distributionId: [{
|
||||
required: true,
|
||||
message: "请选择分销员",
|
||||
trigger: "change",
|
||||
}],
|
||||
});
|
||||
|
||||
const selectedDistributorLabel = computed(() => {
|
||||
if (!rebindForm.value.distributionId) return "";
|
||||
const name = rebindForm.value.distributionNickname || "-";
|
||||
const mobile = rebindForm.value.distributionMobile || "-";
|
||||
return `${name}(${mobile})`;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.query,
|
||||
() => {
|
||||
applyRouteQuery();
|
||||
getDataList();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
applyRouteQuery();
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function applyRouteQuery() {
|
||||
const query = route.query || {};
|
||||
searchForm.distributionId = query.distributionId ? String(query.distributionId) : "";
|
||||
searchForm.distributionMobile = query.distributionMobile ? String(query.distributionMobile) : "";
|
||||
}
|
||||
function formatInvalidTime(row) {
|
||||
return row?.invalidTime || "-";
|
||||
}
|
||||
function formatDetailInvalidTime(detail) {
|
||||
if (!detail || isRelationEffective(detail)) return "-";
|
||||
return detail.invalidTime || "-";
|
||||
}
|
||||
function formatDetailInvalidReason(detail) {
|
||||
if (!detail || isRelationEffective(detail)) return "-";
|
||||
return detail.invalidReason || "客户关系失效";
|
||||
}
|
||||
function isRelationEffective(row) {
|
||||
if (!row) return false;
|
||||
if (row.relationEffective === true) return true;
|
||||
if (row.relationEffective === false) return false;
|
||||
return !["EXPIRED", "UNBOUND"].includes(row.relationStatus);
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
const params = { ...searchForm };
|
||||
if (!params.memberMobile) delete params.memberMobile;
|
||||
if (!params.distributionMobile) delete params.distributionMobile;
|
||||
if (!params.distributionId) delete params.distributionId;
|
||||
if (params.relationEffective === "" || params.relationEffective === null || params.relationEffective === undefined) {
|
||||
delete params.relationEffective;
|
||||
} else {
|
||||
params.relationEffective = params.relationEffective === true || params.relationEffective === "true";
|
||||
}
|
||||
if (params.hasDeadline === "" || params.hasDeadline === null || params.hasDeadline === undefined) {
|
||||
delete params.hasDeadline;
|
||||
} else {
|
||||
params.hasDeadline = params.hasDeadline === true || params.hasDeadline === "true";
|
||||
}
|
||||
if (bindTimeRange.value && bindTimeRange.value[0] && bindTimeRange.value[1]) {
|
||||
const startTime = new Date(bindTimeRange.value[0]).getTime();
|
||||
const endTime = new Date(bindTimeRange.value[1]).getTime();
|
||||
params.bindTimeStart = unixToDate(startTime / 1000);
|
||||
params.bindTimeEnd = unixToDate(endTime / 1000);
|
||||
}
|
||||
getCustomerRelations(params).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function openDetail(row) {
|
||||
detailVisible.value = true;
|
||||
detailLoading.value = true;
|
||||
detail.value = null;
|
||||
getCustomerRelationDetail(row.id).then((res) => {
|
||||
detailLoading.value = false;
|
||||
if (res.success) {
|
||||
detail.value = res.result;
|
||||
}
|
||||
}).catch(() => {
|
||||
detailLoading.value = false;
|
||||
});
|
||||
}
|
||||
function openLogs(row) {
|
||||
logVisible.value = true;
|
||||
logTarget.value = row;
|
||||
logLoading.value = true;
|
||||
logList.value = [];
|
||||
getCustomerRelationLogs(row.id).then((res) => {
|
||||
logLoading.value = false;
|
||||
if (res.success) {
|
||||
logList.value = res.result || [];
|
||||
}
|
||||
}).catch(() => {
|
||||
logLoading.value = false;
|
||||
});
|
||||
}
|
||||
function formatDistributorCell(nickname, mobile, id) {
|
||||
if ((!nickname || nickname === "-") && !id) return "-";
|
||||
const name = nickname && nickname !== "-" ? nickname : id;
|
||||
if (mobile) return `${name}\n${mobile}`;
|
||||
return name;
|
||||
}
|
||||
function openRebind(row) {
|
||||
rebindForm.value = {
|
||||
memberId: row.memberId,
|
||||
memberNickname: row.memberNickname,
|
||||
memberMobile: row.memberMobile,
|
||||
currentDistributionId: row.distributionId || "",
|
||||
distributionId: "",
|
||||
distributionNickname: "",
|
||||
distributionMobile: "",
|
||||
reason: "",
|
||||
};
|
||||
rebindVisible.value = true;
|
||||
}
|
||||
function onDistributorSelected(row) {
|
||||
rebindForm.value.distributionId = row.id;
|
||||
rebindForm.value.distributionNickname = row.nickname;
|
||||
rebindForm.value.distributionMobile = row.mobile;
|
||||
nextTick(() => {
|
||||
rebindFormRef.value?.validateField("distributionId");
|
||||
});
|
||||
}
|
||||
function clearSelectedDistributor() {
|
||||
rebindForm.value.distributionId = "";
|
||||
rebindForm.value.distributionNickname = "";
|
||||
rebindForm.value.distributionMobile = "";
|
||||
nextTick(() => {
|
||||
rebindFormRef.value?.validateField("distributionId");
|
||||
});
|
||||
}
|
||||
function openUnbind(row) {
|
||||
unbindForm.value = {
|
||||
memberId: row.memberId,
|
||||
memberNickname: row.memberNickname,
|
||||
memberMobile: row.memberMobile,
|
||||
reason: "",
|
||||
};
|
||||
unbindVisible.value = true;
|
||||
}
|
||||
function submitRebind() {
|
||||
rebindFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
if (rebindForm.value.distributionId === rebindForm.value.currentDistributionId) {
|
||||
Message.warning("新分销员不能与当前分销员相同");
|
||||
return;
|
||||
}
|
||||
rebindLoading.value = true;
|
||||
rebindCustomerRelation({
|
||||
memberId: rebindForm.value.memberId,
|
||||
distributionId: rebindForm.value.distributionId,
|
||||
reason: rebindForm.value.reason,
|
||||
}).then((res) => {
|
||||
rebindLoading.value = false;
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
rebindVisible.value = false;
|
||||
getDataList();
|
||||
}
|
||||
if (params.hasDeadline === "" || params.hasDeadline === null || params.hasDeadline === undefined) {
|
||||
delete params.hasDeadline;
|
||||
} else {
|
||||
params.hasDeadline = params.hasDeadline === true || params.hasDeadline === "true";
|
||||
}
|
||||
if (this.bindTimeRange && this.bindTimeRange[0] && this.bindTimeRange[1]) {
|
||||
const startTime = new Date(this.bindTimeRange[0]).getTime();
|
||||
const endTime = new Date(this.bindTimeRange[1]).getTime();
|
||||
params.bindTimeStart = this.$filters.unixToDate(startTime / 1000);
|
||||
params.bindTimeEnd = this.$filters.unixToDate(endTime / 1000);
|
||||
}
|
||||
getCustomerRelations(params).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
openDetail(row) {
|
||||
this.detailVisible = true;
|
||||
this.detailLoading = true;
|
||||
this.detail = null;
|
||||
getCustomerRelationDetail(row.id).then((res) => {
|
||||
this.detailLoading = false;
|
||||
if (res.success) {
|
||||
this.detail = res.result;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.detailLoading = false;
|
||||
});
|
||||
},
|
||||
openLogs(row) {
|
||||
this.logVisible = true;
|
||||
this.logTarget = row;
|
||||
this.logLoading = true;
|
||||
this.logList = [];
|
||||
getCustomerRelationLogs(row.id).then((res) => {
|
||||
this.logLoading = false;
|
||||
if (res.success) {
|
||||
this.logList = res.result || [];
|
||||
}
|
||||
}).catch(() => {
|
||||
this.logLoading = false;
|
||||
});
|
||||
},
|
||||
formatDistributorCell(nickname, mobile, id) {
|
||||
if ((!nickname || nickname === "-") && !id) return "-";
|
||||
const name = nickname && nickname !== "-" ? nickname : id;
|
||||
if (mobile) return `${name}\n${mobile}`;
|
||||
return name;
|
||||
},
|
||||
openRebind(row) {
|
||||
this.rebindForm = {
|
||||
memberId: row.memberId,
|
||||
memberNickname: row.memberNickname,
|
||||
memberMobile: row.memberMobile,
|
||||
currentDistributionId: row.distributionId || "",
|
||||
distributionId: "",
|
||||
distributionNickname: "",
|
||||
distributionMobile: "",
|
||||
reason: "",
|
||||
};
|
||||
this.rebindVisible = true;
|
||||
},
|
||||
onDistributorSelected(row) {
|
||||
this.rebindForm.distributionId = row.id;
|
||||
this.rebindForm.distributionNickname = row.nickname;
|
||||
this.rebindForm.distributionMobile = row.mobile;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.rebindForm?.validateField("distributionId");
|
||||
});
|
||||
},
|
||||
clearSelectedDistributor() {
|
||||
this.rebindForm.distributionId = "";
|
||||
this.rebindForm.distributionNickname = "";
|
||||
this.rebindForm.distributionMobile = "";
|
||||
this.$nextTick(() => {
|
||||
this.$refs.rebindForm?.validateField("distributionId");
|
||||
});
|
||||
},
|
||||
openUnbind(row) {
|
||||
this.unbindForm = {
|
||||
memberId: row.memberId,
|
||||
memberNickname: row.memberNickname,
|
||||
memberMobile: row.memberMobile,
|
||||
reason: "",
|
||||
};
|
||||
this.unbindVisible = true;
|
||||
},
|
||||
submitRebind() {
|
||||
this.$refs.rebindForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
if (this.rebindForm.distributionId === this.rebindForm.currentDistributionId) {
|
||||
this.$Message.warning("新分销员不能与当前分销员相同");
|
||||
return;
|
||||
}
|
||||
this.rebindLoading = true;
|
||||
rebindCustomerRelation({
|
||||
memberId: this.rebindForm.memberId,
|
||||
distributionId: this.rebindForm.distributionId,
|
||||
reason: this.rebindForm.reason,
|
||||
}).then((res) => {
|
||||
this.rebindLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.rebindVisible = false;
|
||||
this.getDataList();
|
||||
}
|
||||
}).catch(() => {
|
||||
this.rebindLoading = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
submitUnbind() {
|
||||
this.unbindLoading = true;
|
||||
unbindCustomerRelation({
|
||||
memberId: this.unbindForm.memberId,
|
||||
reason: this.unbindForm.reason,
|
||||
}).then((res) => {
|
||||
this.unbindLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.unbindVisible = false;
|
||||
this.getDataList();
|
||||
}
|
||||
}).catch(() => {
|
||||
this.unbindLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}).catch(() => {
|
||||
rebindLoading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
function submitUnbind() {
|
||||
unbindLoading.value = true;
|
||||
unbindCustomerRelation({
|
||||
memberId: unbindForm.value.memberId,
|
||||
reason: unbindForm.value.reason,
|
||||
}).then((res) => {
|
||||
unbindLoading.value = false;
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
unbindVisible.value = false;
|
||||
getDataList();
|
||||
}
|
||||
}).catch(() => {
|
||||
unbindLoading.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -189,206 +189,206 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from "vue";
|
||||
import { auditDistributionGoods, delDistributionGoods, getDistributionGoods } from "@/api/distribution";
|
||||
import { getBrandListData, getCategoryTree } from "@/api/goods";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
|
||||
export default {
|
||||
name: "distributionGoods",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
currentAuthStatus: "ALL",
|
||||
auditModalVisible: false,
|
||||
auditLoading: false,
|
||||
auditTargetIds: [],
|
||||
auditForm: {
|
||||
authStatus: "PASS",
|
||||
authRemark: "",
|
||||
},
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
goodsName: "",
|
||||
storeName: "",
|
||||
categoryId: "",
|
||||
brandId: "",
|
||||
authStatus: "",
|
||||
},
|
||||
categoryList: [],
|
||||
categoryProps: {
|
||||
value: "id",
|
||||
label: "name",
|
||||
children: "children",
|
||||
checkStrictly: true,
|
||||
emitPath: false,
|
||||
},
|
||||
brandList: [],
|
||||
selectList: [],
|
||||
selectCount: 0,
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
hasPendingAuditSelection() {
|
||||
return this.selectList.some((item) => item.authStatus === "TOBEAUDITED");
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
authStatusText(value) {
|
||||
if (value === "TOBEAUDITED") return "待审核";
|
||||
if (value === "REFUSE") return "审核未通过";
|
||||
return "已审核";
|
||||
},
|
||||
authStatusTagType(value) {
|
||||
if (value === "TOBEAUDITED") return "warning";
|
||||
if (value === "REFUSE") return "danger";
|
||||
return "success";
|
||||
},
|
||||
onAuthStatusChange(name) {
|
||||
this.searchForm.authStatus = name === "ALL" ? "" : name;
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
this.clearSelectAll();
|
||||
},
|
||||
openAudit(row) {
|
||||
this.auditTargetIds = [row.id];
|
||||
this.auditForm = { authStatus: "PASS", authRemark: "" };
|
||||
this.auditModalVisible = true;
|
||||
},
|
||||
openBatchAudit() {
|
||||
const pendingRows = this.selectList.filter((item) => item.authStatus === "TOBEAUDITED");
|
||||
if (!pendingRows.length) {
|
||||
this.$Message.warning("请选择待审核的分销商品");
|
||||
return;
|
||||
defineOptions({ name: "distributionGoods" });
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const currentAuthStatus = ref("ALL");
|
||||
const auditModalVisible = ref(false);
|
||||
const auditLoading = ref(false);
|
||||
const auditTargetIds = ref([]);
|
||||
const auditForm = ref({
|
||||
authStatus: "PASS",
|
||||
authRemark: "",
|
||||
});
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
goodsName: "",
|
||||
storeName: "",
|
||||
categoryId: "",
|
||||
brandId: "",
|
||||
authStatus: "",
|
||||
});
|
||||
const categoryList = ref([]);
|
||||
const categoryProps = reactive({
|
||||
value: "id",
|
||||
label: "name",
|
||||
children: "children",
|
||||
checkStrictly: true,
|
||||
emitPath: false,
|
||||
});
|
||||
const brandList = ref([]);
|
||||
const selectList = ref([]);
|
||||
const selectCount = ref(0);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
const hasPendingAuditSelection = computed(() =>
|
||||
selectList.value.some((item) => item.authStatus === "TOBEAUDITED")
|
||||
);
|
||||
|
||||
function authStatusText(value) {
|
||||
if (value === "TOBEAUDITED") return "待审核";
|
||||
if (value === "REFUSE") return "审核未通过";
|
||||
return "已审核";
|
||||
}
|
||||
function authStatusTagType(value) {
|
||||
if (value === "TOBEAUDITED") return "warning";
|
||||
if (value === "REFUSE") return "danger";
|
||||
return "success";
|
||||
}
|
||||
function onAuthStatusChange(name) {
|
||||
searchForm.authStatus = name === "ALL" ? "" : name;
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
clearSelectAll();
|
||||
}
|
||||
function openAudit(row) {
|
||||
auditTargetIds.value = [row.id];
|
||||
auditForm.value = { authStatus: "PASS", authRemark: "" };
|
||||
auditModalVisible.value = true;
|
||||
}
|
||||
function openBatchAudit() {
|
||||
const pendingRows = selectList.value.filter((item) => item.authStatus === "TOBEAUDITED");
|
||||
if (!pendingRows.length) {
|
||||
Message.warning("请选择待审核的分销商品");
|
||||
return;
|
||||
}
|
||||
auditTargetIds.value = pendingRows.map((item) => item.id);
|
||||
auditForm.value = { authStatus: "PASS", authRemark: "" };
|
||||
auditModalVisible.value = true;
|
||||
}
|
||||
function submitAudit() {
|
||||
if (auditForm.value.authStatus === "REFUSE" && !auditForm.value.authRemark.trim()) {
|
||||
Message.warning("请填写驳回原因");
|
||||
return;
|
||||
}
|
||||
auditLoading.value = true;
|
||||
auditDistributionGoods({
|
||||
ids: auditTargetIds.value,
|
||||
authStatus: auditForm.value.authStatus,
|
||||
authRemark: auditForm.value.authRemark.trim(),
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("审核成功");
|
||||
auditModalVisible.value = false;
|
||||
clearSelectAll();
|
||||
getDataList();
|
||||
}
|
||||
this.auditTargetIds = pendingRows.map((item) => item.id);
|
||||
this.auditForm = { authStatus: "PASS", authRemark: "" };
|
||||
this.auditModalVisible = true;
|
||||
},
|
||||
submitAudit() {
|
||||
if (this.auditForm.authStatus === "REFUSE" && !this.auditForm.authRemark.trim()) {
|
||||
this.$Message.warning("请填写驳回原因");
|
||||
return;
|
||||
})
|
||||
.finally(() => {
|
||||
auditLoading.value = false;
|
||||
});
|
||||
}
|
||||
function goodsTypeText(value) {
|
||||
if (value === "PHYSICAL_GOODS") return "实物商品";
|
||||
if (value === "VIRTUAL_GOODS") return "虚拟商品";
|
||||
if (value === "E_COUPON") return "电子卡券";
|
||||
return "-";
|
||||
}
|
||||
function init() {
|
||||
loadFilterOptions();
|
||||
getDataList();
|
||||
}
|
||||
function loadFilterOptions() {
|
||||
getCategoryTree().then((res) => {
|
||||
if (res.success) {
|
||||
categoryList.value = res.result || [];
|
||||
}
|
||||
});
|
||||
getBrandListData().then((res) => {
|
||||
brandList.value = Array.isArray(res) ? res : res.result || [];
|
||||
});
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
clearSelectAll();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function clearSelectAll() {
|
||||
table.value?.clearSelection();
|
||||
}
|
||||
function changeSelect(e) {
|
||||
selectList.value = e;
|
||||
selectCount.value = e.length;
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getDistributionGoods(searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
this.auditLoading = true;
|
||||
auditDistributionGoods({
|
||||
ids: this.auditTargetIds,
|
||||
authStatus: this.auditForm.authStatus,
|
||||
authRemark: this.auditForm.authRemark.trim(),
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("审核成功");
|
||||
this.auditModalVisible = false;
|
||||
this.clearSelectAll();
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.auditLoading = false;
|
||||
});
|
||||
},
|
||||
goodsTypeText(value) {
|
||||
if (value === "PHYSICAL_GOODS") return "实物商品";
|
||||
if (value === "VIRTUAL_GOODS") return "虚拟商品";
|
||||
if (value === "E_COUPON") return "电子卡券";
|
||||
return "-";
|
||||
},
|
||||
init() {
|
||||
this.loadFilterOptions();
|
||||
this.getDataList();
|
||||
},
|
||||
loadFilterOptions() {
|
||||
getCategoryTree().then((res) => {
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function remove(v) {
|
||||
Modal.confirm({
|
||||
title: "确认下架",
|
||||
content: "您确认要下架么?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
delDistributionGoods(v.id).then((res) => {
|
||||
Modal.remove();
|
||||
if (res.success) {
|
||||
this.categoryList = res.result || [];
|
||||
Message.success("下架成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
getBrandListData().then((res) => {
|
||||
this.brandList = Array.isArray(res) ? res : res.result || [];
|
||||
},
|
||||
});
|
||||
}
|
||||
function delAll() {
|
||||
if (selectCount.value <= 0) {
|
||||
Message.warning("您还未选择要下架的数据");
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: "确认下架",
|
||||
content: "您确认要下架所选的 " + selectCount.value + " 条数据?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
const ids = selectList.value.map((item) => item.id);
|
||||
delDistributionGoods(ids.toString()).then((res) => {
|
||||
Modal.remove();
|
||||
if (res.success) {
|
||||
Message.success("下架成功");
|
||||
clearSelectAll();
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
this.clearSelectAll();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
clearSelectAll() {
|
||||
this.$refs.table?.clearSelection();
|
||||
},
|
||||
changeSelect(e) {
|
||||
this.selectList = e;
|
||||
this.selectCount = e.length;
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getDistributionGoods(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
remove(v) {
|
||||
this.$Modal.confirm({
|
||||
title: "确认下架",
|
||||
content: "您确认要下架么?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
delDistributionGoods(v.id).then((res) => {
|
||||
this.$Modal.remove();
|
||||
if (res.success) {
|
||||
this.$Message.success("下架成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
delAll() {
|
||||
if (this.selectCount <= 0) {
|
||||
this.$Message.warning("您还未选择要下架的数据");
|
||||
return;
|
||||
}
|
||||
this.$Modal.confirm({
|
||||
title: "确认下架",
|
||||
content: "您确认要下架所选的 " + this.selectCount + " 条数据?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
const ids = this.selectList.map((item) => item.id);
|
||||
delDistributionGoods(ids.toString()).then((res) => {
|
||||
this.$Modal.remove();
|
||||
if (res.success) {
|
||||
this.$Message.success("下架成功");
|
||||
this.clearSelectAll();
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -108,12 +108,16 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import {
|
||||
getDistributionGrades,
|
||||
saveAllDistributionGrades,
|
||||
deleteDistributionGrade,
|
||||
} from "@/api/distribution";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
|
||||
defineOptions({ name: "distributionGrade" });
|
||||
|
||||
const MAX_GRADE_COUNT = 5;
|
||||
const GRADE_VALUE_OPTIONS = [1, 2, 3, 4, 5];
|
||||
@@ -154,252 +158,244 @@ const buildDefaultRow = (sortOrder = null) => {
|
||||
};
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "distributionGrade",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
saveLoading: false,
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
normalizeRow(row, index) {
|
||||
const normalized = {
|
||||
...buildDefaultRow(row.sortOrder),
|
||||
...row,
|
||||
enableSalesCondition: row.enableSalesCondition !== false,
|
||||
enableCommissionCondition: row.enableCommissionCondition !== false,
|
||||
enableInviteCondition: row.enableInviteCondition !== false,
|
||||
goodsCommissionRate: toPercent(row.goodsCommissionRate),
|
||||
inviteCommissionRate: toPercent(row.inviteCommissionRate),
|
||||
_key: row._key || row.id || `grade_${index}_${Date.now()}`,
|
||||
};
|
||||
if (this.isBaseGrade(normalized)) {
|
||||
normalized.enableSalesCondition = false;
|
||||
normalized.enableCommissionCondition = false;
|
||||
normalized.enableInviteCondition = false;
|
||||
if (!normalized.gradeName || !normalized.gradeName.trim()) {
|
||||
normalized.gradeName = BASE_GRADE_NAME;
|
||||
}
|
||||
const loading = ref(true);
|
||||
const saveLoading = ref(false);
|
||||
const data = ref([]);
|
||||
|
||||
function normalizeRow(row, index) {
|
||||
const normalized = {
|
||||
...buildDefaultRow(row.sortOrder),
|
||||
...row,
|
||||
enableSalesCondition: row.enableSalesCondition !== false,
|
||||
enableCommissionCondition: row.enableCommissionCondition !== false,
|
||||
enableInviteCondition: row.enableInviteCondition !== false,
|
||||
goodsCommissionRate: toPercent(row.goodsCommissionRate),
|
||||
inviteCommissionRate: toPercent(row.inviteCommissionRate),
|
||||
_key: row._key || row.id || `grade_${index}_${Date.now()}`,
|
||||
};
|
||||
if (isBaseGrade(normalized)) {
|
||||
normalized.enableSalesCondition = false;
|
||||
normalized.enableCommissionCondition = false;
|
||||
normalized.enableInviteCondition = false;
|
||||
if (!normalized.gradeName || !normalized.gradeName.trim()) {
|
||||
normalized.gradeName = BASE_GRADE_NAME;
|
||||
}
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
function isBaseGrade(row) {
|
||||
return row && row.sortOrder === BASE_GRADE_VALUE;
|
||||
}
|
||||
function getSortedRows() {
|
||||
return data.value.slice().sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
||||
}
|
||||
function getGradeLabel(row) {
|
||||
return row.gradeName && row.gradeName.trim() ? row.gradeName.trim() : `等级值${row.sortOrder}`;
|
||||
}
|
||||
function getNextGradeValue() {
|
||||
const used = new Set(data.value.map((item) => item.sortOrder));
|
||||
return GRADE_VALUE_OPTIONS.find((value) => value !== BASE_GRADE_VALUE && !used.has(value)) || null;
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getDistributionGrades().then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = (res.result || [])
|
||||
.slice()
|
||||
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||
.map((item, index) => normalizeRow(item, index));
|
||||
}
|
||||
});
|
||||
}
|
||||
function addRow() {
|
||||
if (data.value.length >= MAX_GRADE_COUNT) {
|
||||
Message.warning("最多添加5个分销等级");
|
||||
return;
|
||||
}
|
||||
const nextGradeValue = getNextGradeValue();
|
||||
if (!nextGradeValue) {
|
||||
Message.warning("等级值1-5已全部使用");
|
||||
return;
|
||||
}
|
||||
data.value.push(buildDefaultRow(nextGradeValue));
|
||||
}
|
||||
function validateGradeProgression() {
|
||||
const rows = getSortedRows();
|
||||
for (let i = 1; i < rows.length; i++) {
|
||||
const lower = rows[i - 1];
|
||||
const higher = rows[i];
|
||||
const lowerLabel = getGradeLabel(lower);
|
||||
const higherLabel = getGradeLabel(higher);
|
||||
if (lower.enableSalesCondition) {
|
||||
if (!higher.enableSalesCondition) {
|
||||
Message.warning(`${higherLabel}必须启用${lowerLabel}已启用的销售额升级条件`);
|
||||
return false;
|
||||
}
|
||||
return normalized;
|
||||
},
|
||||
isBaseGrade(row) {
|
||||
return row && row.sortOrder === BASE_GRADE_VALUE;
|
||||
},
|
||||
getSortedRows() {
|
||||
return this.data.slice().sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0));
|
||||
},
|
||||
getGradeLabel(row) {
|
||||
return row.gradeName && row.gradeName.trim() ? row.gradeName.trim() : `等级值${row.sortOrder}`;
|
||||
},
|
||||
getNextGradeValue() {
|
||||
const used = new Set(this.data.map((item) => item.sortOrder));
|
||||
return GRADE_VALUE_OPTIONS.find((value) => value !== BASE_GRADE_VALUE && !used.has(value)) || null;
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getDistributionGrades().then((res) => {
|
||||
this.loading = false;
|
||||
if (Number(higher.salesThreshold || 0) < Number(lower.salesThreshold || 0)) {
|
||||
Message.warning(`${higherLabel}销售额阈值不能小于${lowerLabel}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (lower.enableCommissionCondition) {
|
||||
if (!higher.enableCommissionCondition) {
|
||||
Message.warning(`${higherLabel}必须启用${lowerLabel}已启用的收益额升级条件`);
|
||||
return false;
|
||||
}
|
||||
if (Number(higher.commissionThreshold || 0) < Number(lower.commissionThreshold || 0)) {
|
||||
Message.warning(`${higherLabel}收益额阈值不能小于${lowerLabel}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (lower.enableInviteCondition) {
|
||||
if (!higher.enableInviteCondition) {
|
||||
Message.warning(`${higherLabel}必须启用${lowerLabel}已启用的邀请人数升级条件`);
|
||||
return false;
|
||||
}
|
||||
if (Number(higher.inviteThreshold || 0) < Number(lower.inviteThreshold || 0)) {
|
||||
Message.warning(`${higherLabel}邀请人数阈值不能小于${lowerLabel}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function validateRows() {
|
||||
if (!data.value.length) {
|
||||
Message.warning("请至少添加一个分销等级");
|
||||
return false;
|
||||
}
|
||||
const usedValues = [];
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const row = data.value[i];
|
||||
if (!row.sortOrder) {
|
||||
Message.warning(`请填写第${i + 1}行等级值`);
|
||||
return false;
|
||||
}
|
||||
if (usedValues.includes(row.sortOrder)) {
|
||||
Message.warning("等级值不能重复");
|
||||
return false;
|
||||
}
|
||||
usedValues.push(row.sortOrder);
|
||||
if (!row.gradeName || !row.gradeName.trim()) {
|
||||
Message.warning(`请填写第${i + 1}行等级名`);
|
||||
return false;
|
||||
}
|
||||
if (isBaseGrade(row)) {
|
||||
continue;
|
||||
}
|
||||
if (!row.enableSalesCondition && !row.enableCommissionCondition && !row.enableInviteCondition) {
|
||||
Message.warning(`第${i + 1}行请至少启用一项升级规则`);
|
||||
return false;
|
||||
}
|
||||
const rowLabel = getGradeLabel(row);
|
||||
if (row.enableSalesCondition && Number(row.salesThreshold || 0) <= 0) {
|
||||
Message.warning(`${rowLabel}销售额必须大于0`);
|
||||
return false;
|
||||
}
|
||||
if (row.enableCommissionCondition && Number(row.commissionThreshold || 0) <= 0) {
|
||||
Message.warning(`${rowLabel}收益额必须大于0`);
|
||||
return false;
|
||||
}
|
||||
if (row.enableInviteCondition && Number(row.inviteThreshold || 0) <= 0) {
|
||||
Message.warning(`${rowLabel}邀请人数必须大于0`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return validateGradeProgression() && validateCrossRates();
|
||||
}
|
||||
function validateCrossRates() {
|
||||
let maxGoods = 0;
|
||||
let maxInvite = 0;
|
||||
for (const row of data.value) {
|
||||
maxGoods = Math.max(maxGoods, Number(row.goodsCommissionRate || 0));
|
||||
maxInvite = Math.max(maxInvite, Number(row.inviteCommissionRate || 0));
|
||||
}
|
||||
if (maxGoods + maxInvite > 100) {
|
||||
Message.warning(
|
||||
`所有等级中最高佣金比例(${maxGoods}%)与最高邀请奖励(${maxInvite}%)之和不能超过100%`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
for (const row of data.value) {
|
||||
const goods = Number(row.goodsCommissionRate || 0);
|
||||
const invite = Number(row.inviteCommissionRate || 0);
|
||||
if (goods + invite > 100) {
|
||||
Message.warning(`${getGradeLabel(row)}的佣金比例与邀请奖励之和不能超过100%`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function buildPayload(row) {
|
||||
const base = isBaseGrade(row);
|
||||
return {
|
||||
id: row.id || undefined,
|
||||
gradeName: row.gradeName.trim(),
|
||||
sortOrder: row.sortOrder,
|
||||
salesThreshold: row.salesThreshold,
|
||||
commissionThreshold: row.commissionThreshold,
|
||||
inviteThreshold: row.inviteThreshold,
|
||||
enableSalesCondition: base ? false : row.enableSalesCondition,
|
||||
enableCommissionCondition: base ? false : row.enableCommissionCondition,
|
||||
enableInviteCondition: base ? false : row.enableInviteCondition,
|
||||
goodsCommissionRate: toFraction(row.goodsCommissionRate),
|
||||
inviteCommissionRate: toFraction(row.inviteCommissionRate),
|
||||
};
|
||||
}
|
||||
async function saveAll() {
|
||||
if (!validateRows()) {
|
||||
return;
|
||||
}
|
||||
saveLoading.value = true;
|
||||
try {
|
||||
const payload = getSortedRows().map((row) => buildPayload(row));
|
||||
const res = await saveAllDistributionGrades(payload);
|
||||
if (res.success) {
|
||||
Message.success("保存成功");
|
||||
getDataList();
|
||||
}
|
||||
} catch (e) {
|
||||
// request layer handles error message
|
||||
} finally {
|
||||
saveLoading.value = false;
|
||||
}
|
||||
}
|
||||
function remove(row, index) {
|
||||
if (isBaseGrade(row)) {
|
||||
Message.warning("默认分销等级不能删除");
|
||||
return;
|
||||
}
|
||||
const name = row.gradeName || `等级值${row.sortOrder || ""}`;
|
||||
if (!row.id) {
|
||||
Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: `您确认要删除 ${name} 吗?`,
|
||||
onOk: () => {
|
||||
data.value.splice(index, 1);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: `您确认要删除 ${name} 吗?`,
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
deleteDistributionGrade(row.id).then((res) => {
|
||||
Modal.remove();
|
||||
if (res.success) {
|
||||
this.data = (res.result || [])
|
||||
.slice()
|
||||
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||
.map((item, index) => this.normalizeRow(item, index));
|
||||
Message.success("删除成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
addRow() {
|
||||
if (this.data.length >= MAX_GRADE_COUNT) {
|
||||
this.$Message.warning("最多添加5个分销等级");
|
||||
return;
|
||||
}
|
||||
const nextGradeValue = this.getNextGradeValue();
|
||||
if (!nextGradeValue) {
|
||||
this.$Message.warning("等级值1-5已全部使用");
|
||||
return;
|
||||
}
|
||||
this.data.push(buildDefaultRow(nextGradeValue));
|
||||
},
|
||||
validateGradeProgression() {
|
||||
const rows = this.getSortedRows();
|
||||
for (let i = 1; i < rows.length; i++) {
|
||||
const lower = rows[i - 1];
|
||||
const higher = rows[i];
|
||||
const lowerLabel = this.getGradeLabel(lower);
|
||||
const higherLabel = this.getGradeLabel(higher);
|
||||
if (lower.enableSalesCondition) {
|
||||
if (!higher.enableSalesCondition) {
|
||||
this.$Message.warning(`${higherLabel}必须启用${lowerLabel}已启用的销售额升级条件`);
|
||||
return false;
|
||||
}
|
||||
if (Number(higher.salesThreshold || 0) < Number(lower.salesThreshold || 0)) {
|
||||
this.$Message.warning(`${higherLabel}销售额阈值不能小于${lowerLabel}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (lower.enableCommissionCondition) {
|
||||
if (!higher.enableCommissionCondition) {
|
||||
this.$Message.warning(`${higherLabel}必须启用${lowerLabel}已启用的收益额升级条件`);
|
||||
return false;
|
||||
}
|
||||
if (Number(higher.commissionThreshold || 0) < Number(lower.commissionThreshold || 0)) {
|
||||
this.$Message.warning(`${higherLabel}收益额阈值不能小于${lowerLabel}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (lower.enableInviteCondition) {
|
||||
if (!higher.enableInviteCondition) {
|
||||
this.$Message.warning(`${higherLabel}必须启用${lowerLabel}已启用的邀请人数升级条件`);
|
||||
return false;
|
||||
}
|
||||
if (Number(higher.inviteThreshold || 0) < Number(lower.inviteThreshold || 0)) {
|
||||
this.$Message.warning(`${higherLabel}邀请人数阈值不能小于${lowerLabel}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateRows() {
|
||||
if (!this.data.length) {
|
||||
this.$Message.warning("请至少添加一个分销等级");
|
||||
return false;
|
||||
}
|
||||
const usedValues = [];
|
||||
for (let i = 0; i < this.data.length; i++) {
|
||||
const row = this.data[i];
|
||||
if (!row.sortOrder) {
|
||||
this.$Message.warning(`请填写第${i + 1}行等级值`);
|
||||
return false;
|
||||
}
|
||||
if (usedValues.includes(row.sortOrder)) {
|
||||
this.$Message.warning("等级值不能重复");
|
||||
return false;
|
||||
}
|
||||
usedValues.push(row.sortOrder);
|
||||
if (!row.gradeName || !row.gradeName.trim()) {
|
||||
this.$Message.warning(`请填写第${i + 1}行等级名`);
|
||||
return false;
|
||||
}
|
||||
if (this.isBaseGrade(row)) {
|
||||
continue;
|
||||
}
|
||||
if (!row.enableSalesCondition && !row.enableCommissionCondition && !row.enableInviteCondition) {
|
||||
this.$Message.warning(`第${i + 1}行请至少启用一项升级规则`);
|
||||
return false;
|
||||
}
|
||||
const rowLabel = this.getGradeLabel(row);
|
||||
if (row.enableSalesCondition && Number(row.salesThreshold || 0) <= 0) {
|
||||
this.$Message.warning(`${rowLabel}销售额必须大于0`);
|
||||
return false;
|
||||
}
|
||||
if (row.enableCommissionCondition && Number(row.commissionThreshold || 0) <= 0) {
|
||||
this.$Message.warning(`${rowLabel}收益额必须大于0`);
|
||||
return false;
|
||||
}
|
||||
if (row.enableInviteCondition && Number(row.inviteThreshold || 0) <= 0) {
|
||||
this.$Message.warning(`${rowLabel}邀请人数必须大于0`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this.validateGradeProgression() && this.validateCrossRates();
|
||||
},
|
||||
validateCrossRates() {
|
||||
let maxGoods = 0;
|
||||
let maxInvite = 0;
|
||||
for (const row of this.data) {
|
||||
maxGoods = Math.max(maxGoods, Number(row.goodsCommissionRate || 0));
|
||||
maxInvite = Math.max(maxInvite, Number(row.inviteCommissionRate || 0));
|
||||
}
|
||||
if (maxGoods + maxInvite > 100) {
|
||||
this.$Message.warning(
|
||||
`所有等级中最高佣金比例(${maxGoods}%)与最高邀请奖励(${maxInvite}%)之和不能超过100%`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
for (const row of this.data) {
|
||||
const goods = Number(row.goodsCommissionRate || 0);
|
||||
const invite = Number(row.inviteCommissionRate || 0);
|
||||
if (goods + invite > 100) {
|
||||
this.$Message.warning(`${this.getGradeLabel(row)}的佣金比例与邀请奖励之和不能超过100%`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
buildPayload(row) {
|
||||
const isBaseGrade = this.isBaseGrade(row);
|
||||
return {
|
||||
id: row.id || undefined,
|
||||
gradeName: row.gradeName.trim(),
|
||||
sortOrder: row.sortOrder,
|
||||
salesThreshold: row.salesThreshold,
|
||||
commissionThreshold: row.commissionThreshold,
|
||||
inviteThreshold: row.inviteThreshold,
|
||||
enableSalesCondition: isBaseGrade ? false : row.enableSalesCondition,
|
||||
enableCommissionCondition: isBaseGrade ? false : row.enableCommissionCondition,
|
||||
enableInviteCondition: isBaseGrade ? false : row.enableInviteCondition,
|
||||
goodsCommissionRate: toFraction(row.goodsCommissionRate),
|
||||
inviteCommissionRate: toFraction(row.inviteCommissionRate),
|
||||
};
|
||||
},
|
||||
async saveAll() {
|
||||
if (!this.validateRows()) {
|
||||
return;
|
||||
}
|
||||
this.saveLoading = true;
|
||||
try {
|
||||
const payload = this.getSortedRows().map((row) => this.buildPayload(row));
|
||||
const res = await saveAllDistributionGrades(payload);
|
||||
if (res.success) {
|
||||
this.$Message.success("保存成功");
|
||||
this.getDataList();
|
||||
}
|
||||
} catch (e) {
|
||||
// request layer handles error message
|
||||
} finally {
|
||||
this.saveLoading = false;
|
||||
}
|
||||
},
|
||||
remove(row, index) {
|
||||
if (this.isBaseGrade(row)) {
|
||||
this.$Message.warning("默认分销等级不能删除");
|
||||
return;
|
||||
}
|
||||
const name = row.gradeName || `等级值${row.sortOrder || ""}`;
|
||||
if (!row.id) {
|
||||
this.$Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: `您确认要删除 ${name} 吗?`,
|
||||
onOk: () => {
|
||||
this.data.splice(index, 1);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: `您确认要删除 ${name} 吗?`,
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
deleteDistributionGrade(row.id).then((res) => {
|
||||
this.$Modal.remove();
|
||||
if (res.success) {
|
||||
this.$Message.success("删除成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.grade-footer {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="110px"
|
||||
@@ -101,115 +101,114 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { getDistributionGradeHistoryPage, getDistributionGrades } from "@/api/distribution";
|
||||
|
||||
export default {
|
||||
name: "distributionGradeHistory",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
memberName: "",
|
||||
distributionMobile: "",
|
||||
gradeId: "",
|
||||
upgradeTimeStart: "",
|
||||
upgradeTimeEnd: "",
|
||||
},
|
||||
upgradeTimeRange: [],
|
||||
gradeOptions: [],
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadGradeOptions();
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
loadGradeOptions() {
|
||||
getDistributionGrades()
|
||||
.then((res) => {
|
||||
if (!res.success) {
|
||||
return;
|
||||
}
|
||||
this.gradeOptions = (res.result || [])
|
||||
.slice()
|
||||
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||
.map((item) => ({
|
||||
value: String(item.sortOrder),
|
||||
label: item.gradeName || `等级值${item.sortOrder}`,
|
||||
}));
|
||||
});
|
||||
},
|
||||
formatGrade(name, gradeId) {
|
||||
if (name) {
|
||||
return name;
|
||||
defineOptions({ name: "distributionGradeHistory" });
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
memberName: "",
|
||||
distributionMobile: "",
|
||||
gradeId: "",
|
||||
upgradeTimeStart: "",
|
||||
upgradeTimeEnd: "",
|
||||
});
|
||||
const upgradeTimeRange = ref([]);
|
||||
const gradeOptions = ref([]);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
loadGradeOptions();
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function loadGradeOptions() {
|
||||
getDistributionGrades()
|
||||
.then((res) => {
|
||||
if (!res.success) {
|
||||
return;
|
||||
}
|
||||
if (gradeId) {
|
||||
return `等级值${gradeId}`;
|
||||
gradeOptions.value = (res.result || [])
|
||||
.slice()
|
||||
.sort((a, b) => (a.sortOrder || 0) - (b.sortOrder || 0))
|
||||
.map((item) => ({
|
||||
value: String(item.sortOrder),
|
||||
label: item.gradeName || `等级值${item.sortOrder}`,
|
||||
}));
|
||||
});
|
||||
}
|
||||
function formatGrade(name, gradeId) {
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
if (gradeId) {
|
||||
return `等级值${gradeId}`;
|
||||
}
|
||||
return "-";
|
||||
}
|
||||
function formatRate(value) {
|
||||
const num = Number(value);
|
||||
if (!Number.isFinite(num)) {
|
||||
return "-";
|
||||
}
|
||||
return `${(num * 100).toFixed(2).replace(/\.?0+$/, "") || "0"}%`;
|
||||
}
|
||||
function applyUpgradeTimeRange() {
|
||||
if (upgradeTimeRange.value && upgradeTimeRange.value.length === 2) {
|
||||
searchForm.value.upgradeTimeStart = `${upgradeTimeRange.value[0]} 00:00:00`;
|
||||
searchForm.value.upgradeTimeEnd = `${upgradeTimeRange.value[1]} 23:59:59`;
|
||||
} else {
|
||||
searchForm.value.upgradeTimeStart = "";
|
||||
searchForm.value.upgradeTimeEnd = "";
|
||||
}
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleReset() {
|
||||
searchForm.value = {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
memberName: "",
|
||||
distributionMobile: "",
|
||||
gradeId: "",
|
||||
upgradeTimeStart: "",
|
||||
upgradeTimeEnd: "",
|
||||
};
|
||||
upgradeTimeRange.value = [];
|
||||
getDataList();
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
applyUpgradeTimeRange();
|
||||
loading.value = true;
|
||||
getDistributionGradeHistoryPage(searchForm.value)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
return "-";
|
||||
},
|
||||
formatRate(value) {
|
||||
const num = Number(value);
|
||||
if (!Number.isFinite(num)) {
|
||||
return "-";
|
||||
}
|
||||
return `${(num * 100).toFixed(2).replace(/\.?0+$/, "") || "0"}%`;
|
||||
},
|
||||
applyUpgradeTimeRange() {
|
||||
if (this.upgradeTimeRange && this.upgradeTimeRange.length === 2) {
|
||||
this.searchForm.upgradeTimeStart = `${this.upgradeTimeRange[0]} 00:00:00`;
|
||||
this.searchForm.upgradeTimeEnd = `${this.upgradeTimeRange[1]} 23:59:59`;
|
||||
} else {
|
||||
this.searchForm.upgradeTimeStart = "";
|
||||
this.searchForm.upgradeTimeEnd = "";
|
||||
}
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleReset() {
|
||||
this.searchForm = {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
memberName: "",
|
||||
distributionMobile: "",
|
||||
gradeId: "",
|
||||
upgradeTimeStart: "",
|
||||
upgradeTimeEnd: "",
|
||||
};
|
||||
this.upgradeTimeRange = [];
|
||||
this.getDataList();
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.applyUpgradeTimeRange();
|
||||
this.loading = true;
|
||||
getDistributionGradeHistoryPage(this.searchForm)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -43,85 +43,82 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import {
|
||||
downloadDistributionImportFailItems,
|
||||
getDistributionImportBatches,
|
||||
} from "@/api/distribution";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
export default {
|
||||
name: "distributionImportRecord",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
statusLabel(status) {
|
||||
const map = {
|
||||
PROCESSING: "处理中",
|
||||
DONE: "已完成",
|
||||
FAILED: "失败",
|
||||
};
|
||||
return map[status] || status || "-";
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getDistributionImportBatches(this.searchForm)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
defineOptions({ name: "distributionImportRecord" });
|
||||
|
||||
const loading = ref(false);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function statusLabel(status) {
|
||||
const map = {
|
||||
PROCESSING: "处理中",
|
||||
DONE: "已完成",
|
||||
FAILED: "失败",
|
||||
};
|
||||
return map[status] || status || "-";
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getDistributionImportBatches(searchForm)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function downloadFailList(row) {
|
||||
if (!row || !row.failCount) return;
|
||||
downloadDistributionImportFailItems(row.id)
|
||||
.then((blob) => {
|
||||
if (blob && blob.type && blob.type.includes("application/json")) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
try {
|
||||
const result = JSON.parse(reader.result);
|
||||
Message.error(result.message || "下载失败");
|
||||
} catch (e) {
|
||||
Message.error("下载失败");
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
downloadFailList(row) {
|
||||
if (!row || !row.failCount) return;
|
||||
downloadDistributionImportFailItems(row.id)
|
||||
.then((blob) => {
|
||||
if (blob && blob.type && blob.type.includes("application/json")) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
try {
|
||||
const result = JSON.parse(reader.result);
|
||||
this.$Message.error(result.message || "下载失败");
|
||||
} catch (e) {
|
||||
this.$Message.error("下载失败");
|
||||
}
|
||||
};
|
||||
reader.readAsText(blob);
|
||||
return;
|
||||
}
|
||||
downloadBlob(blob, `分销员导入失败列表_${row.id}.xlsx`);
|
||||
this.$Message.success("下载成功");
|
||||
})
|
||||
.catch(() => {
|
||||
this.$Message.error("下载失败");
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
reader.readAsText(blob);
|
||||
return;
|
||||
}
|
||||
downloadBlob(blob, `分销员导入失败列表_${row.id}.xlsx`);
|
||||
Message.success("下载成功");
|
||||
})
|
||||
.catch(() => {
|
||||
Message.error("下载失败");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -159,135 +159,140 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { getDistributionOrder } from "@/api/distribution";
|
||||
import { orderStatusList } from "./dataJson";
|
||||
import { getShopListData } from "@/api/shops";
|
||||
import { unixToDate, unitPrice } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "distributionOrder",
|
||||
data() {
|
||||
return {
|
||||
timeRange: [],
|
||||
orderStatusList,
|
||||
shopList: [],
|
||||
distributionId: this.$route.query.id,
|
||||
currentStatus: "ALL",
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
distributionOrderStatus: "",
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
$route(e) {
|
||||
this.distributionId = e.query.id ? e.query.id : undefined;
|
||||
this.getDataList();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getDataList();
|
||||
this.getShopList();
|
||||
},
|
||||
onStatusTabChange(name) {
|
||||
this.currentStatus = name;
|
||||
this.searchForm.distributionOrderStatus = name === "ALL" ? "" : name;
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
if (this.distributionId) {
|
||||
this.searchForm.distributionId = this.distributionId;
|
||||
} else {
|
||||
delete this.searchForm.distributionId;
|
||||
}
|
||||
this.loading = true;
|
||||
if (this.timeRange && this.timeRange[0] && this.timeRange[1]) {
|
||||
const startTime = new Date(this.timeRange[0]).getTime();
|
||||
const endTime = new Date(this.timeRange[1]).getTime();
|
||||
this.searchForm.startTime = this.$filters.unixToDate(startTime / 1000);
|
||||
this.searchForm.endTime = this.$filters.unixToDate(endTime / 1000);
|
||||
} else {
|
||||
this.searchForm.startTime = null;
|
||||
this.searchForm.endTime = null;
|
||||
}
|
||||
getDistributionOrder(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
getShopList(val) {
|
||||
const params = {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
storeName: val || "",
|
||||
};
|
||||
getShopListData(params).then((res) => {
|
||||
this.shopList = res.result.records;
|
||||
});
|
||||
},
|
||||
searchChange(val) {
|
||||
this.getShopList(val);
|
||||
},
|
||||
filterStatus(status) {
|
||||
const arr = [
|
||||
{ status: "NO_COMPLETED", title: "未完成" },
|
||||
{ status: "COMPLETE", title: "完成" },
|
||||
{ status: "REFUND", title: "退款" },
|
||||
];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].status === status) {
|
||||
return arr[i].title;
|
||||
}
|
||||
}
|
||||
return "未完成";
|
||||
},
|
||||
filterStatusTagType(status) {
|
||||
const arr = [
|
||||
{ status: "NO_COMPLETED", type: "warning" },
|
||||
{ status: "COMPLETE", type: "success" },
|
||||
{ status: "REFUND", type: "danger" },
|
||||
];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].status === status) {
|
||||
return arr[i].type;
|
||||
}
|
||||
}
|
||||
return "warning";
|
||||
},
|
||||
formatCommission(amount) {
|
||||
if (amount === null || amount === undefined || amount === "") {
|
||||
return "-";
|
||||
}
|
||||
return this.$filters.unitPrice(amount, "¥");
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "distributionOrder" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 模板 el-form / el-table 的 ref(原 ref="searchForm" 与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const timeRange = ref([]);
|
||||
const shopList = ref([]);
|
||||
const distributionId = ref(route.query.id);
|
||||
const currentStatus = ref("ALL");
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
distributionOrderStatus: "",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
watch(
|
||||
() => route.query,
|
||||
(query) => {
|
||||
distributionId.value = query.id ? query.id : undefined;
|
||||
getDataList();
|
||||
}
|
||||
);
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
getShopList();
|
||||
}
|
||||
function onStatusTabChange(name) {
|
||||
currentStatus.value = name;
|
||||
searchForm.distributionOrderStatus = name === "ALL" ? "" : name;
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
if (distributionId.value) {
|
||||
searchForm.distributionId = distributionId.value;
|
||||
} else {
|
||||
delete searchForm.distributionId;
|
||||
}
|
||||
loading.value = true;
|
||||
if (timeRange.value && timeRange.value[0] && timeRange.value[1]) {
|
||||
const startTime = new Date(timeRange.value[0]).getTime();
|
||||
const endTime = new Date(timeRange.value[1]).getTime();
|
||||
searchForm.startTime = unixToDate(startTime / 1000);
|
||||
searchForm.endTime = unixToDate(endTime / 1000);
|
||||
} else {
|
||||
searchForm.startTime = null;
|
||||
searchForm.endTime = null;
|
||||
}
|
||||
getDistributionOrder(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function getShopList(val) {
|
||||
const params = {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
storeName: val || "",
|
||||
};
|
||||
getShopListData(params).then((res) => {
|
||||
shopList.value = res.result.records;
|
||||
});
|
||||
}
|
||||
function searchChange(val) {
|
||||
getShopList(val);
|
||||
}
|
||||
function filterStatus(status) {
|
||||
const arr = [
|
||||
{ status: "NO_COMPLETED", title: "未完成" },
|
||||
{ status: "COMPLETE", title: "完成" },
|
||||
{ status: "REFUND", title: "退款" },
|
||||
];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].status === status) {
|
||||
return arr[i].title;
|
||||
}
|
||||
}
|
||||
return "未完成";
|
||||
}
|
||||
function filterStatusTagType(status) {
|
||||
const arr = [
|
||||
{ status: "NO_COMPLETED", type: "warning" },
|
||||
{ status: "COMPLETE", type: "success" },
|
||||
{ status: "REFUND", type: "danger" },
|
||||
];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i].status === status) {
|
||||
return arr[i].type;
|
||||
}
|
||||
}
|
||||
return "warning";
|
||||
}
|
||||
function formatCommission(amount) {
|
||||
if (amount === null || amount === undefined || amount === "") {
|
||||
return "-";
|
||||
}
|
||||
return unitPrice(amount, "¥");
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -177,19 +177,23 @@
|
||||
<el-button type="primary" :loading="planSaveLoading" @click="savePlan">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<sku-select ref="skuSelect" @selectedGoodsData="selectedGoodsData" />
|
||||
<sku-select ref="skuSelectRef" @selectedGoodsData="selectedGoodsData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref, watch } from "vue";
|
||||
import {
|
||||
getCurrentRecruitPlan,
|
||||
saveRecruitPlan,
|
||||
} from "@/api/distribution";
|
||||
import { Message } from "@/utils/message";
|
||||
import uploadPicInput from "@/components/lili/upload-pic-input";
|
||||
import tinymec from "@/components/editor/index.vue";
|
||||
import editor from "@/components/editor/index.vue";
|
||||
import skuSelect from "@/components/lili-dialog";
|
||||
|
||||
defineOptions({ name: "distributionRecruit" });
|
||||
|
||||
const MAX_RECRUIT_FIELDS = 5;
|
||||
|
||||
const FIELD_TYPE_OPTIONS = [
|
||||
@@ -231,228 +235,239 @@ const buildDefaultField = () => ({
|
||||
placeholder: "",
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "distributionRecruit",
|
||||
components: { uploadPicInput, editor: tinymec, skuSelect },
|
||||
data() {
|
||||
return {
|
||||
planSaveLoading: false,
|
||||
editorReady: true,
|
||||
planForm: buildDefaultPlan(),
|
||||
recruitFields: [],
|
||||
recruitGoodsList: [],
|
||||
fieldTypeOptions: FIELD_TYPE_OPTIONS,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadPlan();
|
||||
},
|
||||
watch: {
|
||||
"planForm.applyMode"(value) {
|
||||
if (value !== "MANUAL") {
|
||||
this.planForm.requireApplyInfo = false;
|
||||
this.recruitFields = [];
|
||||
}
|
||||
},
|
||||
"planForm.requireApplyInfo"(value) {
|
||||
if (!value) {
|
||||
this.recruitFields = [];
|
||||
}
|
||||
},
|
||||
"planForm.joinConditionType"(value) {
|
||||
if (value !== "CONDITIONAL") {
|
||||
this.planForm.requirePurchaseGoods = false;
|
||||
this.planForm.requireSelfPurchaseAmount = false;
|
||||
this.planForm.requireConsumeCount = false;
|
||||
this.recruitGoodsList = [];
|
||||
}
|
||||
},
|
||||
"planForm.requirePurchaseGoods"(value) {
|
||||
if (!value) {
|
||||
this.recruitGoodsList = [];
|
||||
}
|
||||
},
|
||||
"planForm.requireSelfPurchaseAmount"(value) {
|
||||
if (value && (!this.planForm.minSelfPurchaseAmount || this.planForm.minSelfPurchaseAmount < 0.01)) {
|
||||
this.planForm.minSelfPurchaseAmount = 0.01;
|
||||
}
|
||||
},
|
||||
"planForm.requireConsumeCount"(value) {
|
||||
if (value && (!this.planForm.minConsumeCount || this.planForm.minConsumeCount < 1)) {
|
||||
this.planForm.minConsumeCount = 1;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
normalizeFields(fields) {
|
||||
if (!fields || !fields.length) {
|
||||
return [];
|
||||
}
|
||||
return fields.map((item, index) => ({
|
||||
...buildDefaultField(),
|
||||
...item,
|
||||
_key: item._key || item.id || `field_${index}_${Date.now()}`,
|
||||
required: item.required !== false,
|
||||
}));
|
||||
},
|
||||
addRecruitField() {
|
||||
if (this.recruitFields.length >= MAX_RECRUIT_FIELDS) {
|
||||
this.$Message.warning("最多添加5个字段");
|
||||
return;
|
||||
}
|
||||
this.recruitFields.push(buildDefaultField());
|
||||
},
|
||||
removeRecruitField(index) {
|
||||
this.recruitFields.splice(index, 1);
|
||||
},
|
||||
validateRecruitFields() {
|
||||
if (!this.planForm.requireApplyInfo) {
|
||||
return true;
|
||||
}
|
||||
if (!this.recruitFields.length) {
|
||||
this.$Message.warning("请至少添加一个申请字段");
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < this.recruitFields.length; i++) {
|
||||
const field = this.recruitFields[i];
|
||||
if (!field.fieldName || !field.fieldName.trim()) {
|
||||
this.$Message.warning(`请填写字段${i + 1}名称`);
|
||||
return false;
|
||||
const skuSelectRef = ref(null);
|
||||
|
||||
const planSaveLoading = ref(false);
|
||||
const editorReady = ref(true);
|
||||
const planForm = ref(buildDefaultPlan());
|
||||
const recruitFields = ref([]);
|
||||
const recruitGoodsList = ref([]);
|
||||
const fieldTypeOptions = ref(FIELD_TYPE_OPTIONS);
|
||||
|
||||
onMounted(() => {
|
||||
loadPlan();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => planForm.value.applyMode,
|
||||
(value) => {
|
||||
if (value !== "MANUAL") {
|
||||
planForm.value.requireApplyInfo = false;
|
||||
recruitFields.value = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => planForm.value.requireApplyInfo,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
recruitFields.value = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => planForm.value.joinConditionType,
|
||||
(value) => {
|
||||
if (value !== "CONDITIONAL") {
|
||||
planForm.value.requirePurchaseGoods = false;
|
||||
planForm.value.requireSelfPurchaseAmount = false;
|
||||
planForm.value.requireConsumeCount = false;
|
||||
recruitGoodsList.value = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => planForm.value.requirePurchaseGoods,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
recruitGoodsList.value = [];
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => planForm.value.requireSelfPurchaseAmount,
|
||||
(value) => {
|
||||
if (value && (!planForm.value.minSelfPurchaseAmount || planForm.value.minSelfPurchaseAmount < 0.01)) {
|
||||
planForm.value.minSelfPurchaseAmount = 0.01;
|
||||
}
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => planForm.value.requireConsumeCount,
|
||||
(value) => {
|
||||
if (value && (!planForm.value.minConsumeCount || planForm.value.minConsumeCount < 1)) {
|
||||
planForm.value.minConsumeCount = 1;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
function normalizeFields(fields) {
|
||||
if (!fields || !fields.length) {
|
||||
return [];
|
||||
}
|
||||
return fields.map((item, index) => ({
|
||||
...buildDefaultField(),
|
||||
...item,
|
||||
_key: item._key || item.id || `field_${index}_${Date.now()}`,
|
||||
required: item.required !== false,
|
||||
}));
|
||||
}
|
||||
function addRecruitField() {
|
||||
if (recruitFields.value.length >= MAX_RECRUIT_FIELDS) {
|
||||
Message.warning("最多添加5个字段");
|
||||
return;
|
||||
}
|
||||
recruitFields.value.push(buildDefaultField());
|
||||
}
|
||||
function removeRecruitField(index) {
|
||||
recruitFields.value.splice(index, 1);
|
||||
}
|
||||
function validateRecruitFields() {
|
||||
if (!planForm.value.requireApplyInfo) {
|
||||
return true;
|
||||
}
|
||||
if (!recruitFields.value.length) {
|
||||
Message.warning("请至少添加一个申请字段");
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < recruitFields.value.length; i++) {
|
||||
const field = recruitFields.value[i];
|
||||
if (!field.fieldName || !field.fieldName.trim()) {
|
||||
Message.warning(`请填写字段${i + 1}名称`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function validateJoinConditions() {
|
||||
if (planForm.value.joinConditionType !== "CONDITIONAL") {
|
||||
return true;
|
||||
}
|
||||
const {
|
||||
requirePurchaseGoods,
|
||||
requireSelfPurchaseAmount,
|
||||
requireConsumeCount,
|
||||
} = planForm.value;
|
||||
if (!requirePurchaseGoods && !requireSelfPurchaseAmount && !requireConsumeCount) {
|
||||
Message.warning("请至少选择一个加入条件");
|
||||
return false;
|
||||
}
|
||||
if (requirePurchaseGoods && !recruitGoodsList.value.length) {
|
||||
Message.warning("请选择指定商品");
|
||||
return false;
|
||||
}
|
||||
if (requireSelfPurchaseAmount && planForm.value.minSelfPurchaseAmount < 0.01) {
|
||||
Message.warning("自购金额最低设置为0.01元");
|
||||
return false;
|
||||
}
|
||||
if (requireConsumeCount && planForm.value.minConsumeCount < 1) {
|
||||
Message.warning("消费笔数最低设置为1笔");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function openGoodsSelect() {
|
||||
skuSelectRef.value.open("goods");
|
||||
const data = recruitGoodsList.value.map((item) => ({
|
||||
id: item.skuId,
|
||||
goodsId: item.goodsId,
|
||||
goodsName: item.goodsName,
|
||||
sn: item.sn,
|
||||
simpleSpecs: item.simpleSpecs,
|
||||
price: item.price,
|
||||
quantity: item.quantity,
|
||||
small: item.thumbnail,
|
||||
}));
|
||||
skuSelectRef.value.goodsData = data;
|
||||
}
|
||||
function selectedGoodsData(items) {
|
||||
recruitGoodsList.value = (items || []).map((item) => ({
|
||||
skuId: item.id,
|
||||
goodsId: item.goodsId,
|
||||
goodsName: item.goodsName,
|
||||
sn: item.sn || "",
|
||||
simpleSpecs: item.simpleSpecs || "",
|
||||
price: item.price,
|
||||
quantity: item.quantity,
|
||||
thumbnail: item.small || item.thumbnail || "",
|
||||
}));
|
||||
}
|
||||
function removeRecruitGoods(index) {
|
||||
recruitGoodsList.value.splice(index, 1);
|
||||
}
|
||||
function buildSavePayload() {
|
||||
const fields = planForm.value.requireApplyInfo
|
||||
? recruitFields.value.map((field, index) => ({
|
||||
id: field.id,
|
||||
fieldName: field.fieldName.trim(),
|
||||
fieldType: field.fieldType,
|
||||
required: field.required !== false,
|
||||
placeholder: field.placeholder || "",
|
||||
sortOrder: index,
|
||||
}))
|
||||
: [];
|
||||
return {
|
||||
...planForm.value,
|
||||
id: planForm.value.id || undefined,
|
||||
publishStatus: true,
|
||||
posterEnabled: !!(planForm.value.posterBackground && planForm.value.posterBackground.trim()),
|
||||
recruitGoods: planForm.value.requirePurchaseGoods
|
||||
? recruitGoodsList.value.map((item) => ({
|
||||
goodsId: item.goodsId,
|
||||
skuId: item.skuId,
|
||||
goodsName: item.goodsName,
|
||||
sn: item.sn,
|
||||
simpleSpecs: item.simpleSpecs,
|
||||
thumbnail: item.thumbnail,
|
||||
price: item.price,
|
||||
}))
|
||||
: [],
|
||||
goodsIds: planForm.value.requirePurchaseGoods
|
||||
? recruitGoodsList.value.map((item) => item.goodsId)
|
||||
: [],
|
||||
fields,
|
||||
};
|
||||
}
|
||||
function applyPlanResult(result) {
|
||||
planForm.value = { ...buildDefaultPlan(), ...result };
|
||||
recruitFields.value = normalizeFields(result.fields);
|
||||
recruitGoodsList.value = (result.recruitGoods || []).map((item) => ({
|
||||
goodsId: item.goodsId,
|
||||
skuId: item.skuId,
|
||||
goodsName: item.goodsName,
|
||||
sn: item.sn || "",
|
||||
simpleSpecs: item.simpleSpecs || "",
|
||||
thumbnail: item.thumbnail,
|
||||
price: item.price,
|
||||
}));
|
||||
}
|
||||
function loadPlan() {
|
||||
getCurrentRecruitPlan().then((res) => {
|
||||
if (res.success && res.result) {
|
||||
applyPlanResult(res.result);
|
||||
}
|
||||
});
|
||||
}
|
||||
function savePlan() {
|
||||
if (!validateRecruitFields() || !validateJoinConditions()) {
|
||||
return;
|
||||
}
|
||||
planSaveLoading.value = true;
|
||||
saveRecruitPlan(buildSavePayload())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("保存成功");
|
||||
if (res.result) {
|
||||
applyPlanResult(res.result);
|
||||
} else {
|
||||
loadPlan();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
validateJoinConditions() {
|
||||
if (this.planForm.joinConditionType !== "CONDITIONAL") {
|
||||
return true;
|
||||
}
|
||||
const {
|
||||
requirePurchaseGoods,
|
||||
requireSelfPurchaseAmount,
|
||||
requireConsumeCount,
|
||||
} = this.planForm;
|
||||
if (!requirePurchaseGoods && !requireSelfPurchaseAmount && !requireConsumeCount) {
|
||||
this.$Message.warning("请至少选择一个加入条件");
|
||||
return false;
|
||||
}
|
||||
if (requirePurchaseGoods && !this.recruitGoodsList.length) {
|
||||
this.$Message.warning("请选择指定商品");
|
||||
return false;
|
||||
}
|
||||
if (requireSelfPurchaseAmount && this.planForm.minSelfPurchaseAmount < 0.01) {
|
||||
this.$Message.warning("自购金额最低设置为0.01元");
|
||||
return false;
|
||||
}
|
||||
if (requireConsumeCount && this.planForm.minConsumeCount < 1) {
|
||||
this.$Message.warning("消费笔数最低设置为1笔");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
openGoodsSelect() {
|
||||
this.$refs.skuSelect.open("goods");
|
||||
const data = this.recruitGoodsList.map((item) => ({
|
||||
id: item.skuId,
|
||||
goodsId: item.goodsId,
|
||||
goodsName: item.goodsName,
|
||||
sn: item.sn,
|
||||
simpleSpecs: item.simpleSpecs,
|
||||
price: item.price,
|
||||
quantity: item.quantity,
|
||||
small: item.thumbnail,
|
||||
}));
|
||||
this.$refs.skuSelect.goodsData = data;
|
||||
},
|
||||
selectedGoodsData(items) {
|
||||
this.recruitGoodsList = (items || []).map((item) => ({
|
||||
skuId: item.id,
|
||||
goodsId: item.goodsId,
|
||||
goodsName: item.goodsName,
|
||||
sn: item.sn || "",
|
||||
simpleSpecs: item.simpleSpecs || "",
|
||||
price: item.price,
|
||||
quantity: item.quantity,
|
||||
thumbnail: item.small || item.thumbnail || "",
|
||||
}));
|
||||
},
|
||||
removeRecruitGoods(index) {
|
||||
this.recruitGoodsList.splice(index, 1);
|
||||
},
|
||||
buildSavePayload() {
|
||||
const fields = this.planForm.requireApplyInfo
|
||||
? this.recruitFields.map((field, index) => ({
|
||||
id: field.id,
|
||||
fieldName: field.fieldName.trim(),
|
||||
fieldType: field.fieldType,
|
||||
required: field.required !== false,
|
||||
placeholder: field.placeholder || "",
|
||||
sortOrder: index,
|
||||
}))
|
||||
: [];
|
||||
return {
|
||||
...this.planForm,
|
||||
id: this.planForm.id || undefined,
|
||||
publishStatus: true,
|
||||
posterEnabled: !!(this.planForm.posterBackground && this.planForm.posterBackground.trim()),
|
||||
recruitGoods: this.planForm.requirePurchaseGoods
|
||||
? this.recruitGoodsList.map((item) => ({
|
||||
goodsId: item.goodsId,
|
||||
skuId: item.skuId,
|
||||
goodsName: item.goodsName,
|
||||
sn: item.sn,
|
||||
simpleSpecs: item.simpleSpecs,
|
||||
thumbnail: item.thumbnail,
|
||||
price: item.price,
|
||||
}))
|
||||
: [],
|
||||
goodsIds: this.planForm.requirePurchaseGoods
|
||||
? this.recruitGoodsList.map((item) => item.goodsId)
|
||||
: [],
|
||||
fields,
|
||||
};
|
||||
},
|
||||
applyPlanResult(result) {
|
||||
this.planForm = { ...buildDefaultPlan(), ...result };
|
||||
this.recruitFields = this.normalizeFields(result.fields);
|
||||
this.recruitGoodsList = (result.recruitGoods || []).map((item) => ({
|
||||
goodsId: item.goodsId,
|
||||
skuId: item.skuId,
|
||||
goodsName: item.goodsName,
|
||||
sn: item.sn || "",
|
||||
simpleSpecs: item.simpleSpecs || "",
|
||||
thumbnail: item.thumbnail,
|
||||
price: item.price,
|
||||
}));
|
||||
},
|
||||
loadPlan() {
|
||||
getCurrentRecruitPlan().then((res) => {
|
||||
if (res.success && res.result) {
|
||||
this.applyPlanResult(res.result);
|
||||
}
|
||||
});
|
||||
},
|
||||
savePlan() {
|
||||
if (!this.validateRecruitFields() || !this.validateJoinConditions()) {
|
||||
return;
|
||||
}
|
||||
this.planSaveLoading = true;
|
||||
saveRecruitPlan(this.buildSavePayload())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("保存成功");
|
||||
if (res.result) {
|
||||
this.applyPlanResult(res.result);
|
||||
} else {
|
||||
this.loadPlan();
|
||||
}
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.planSaveLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.finally(() => {
|
||||
planSaveLoading.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<div style="background-color: #fff">
|
||||
<el-form ref="formRef" :model="form" label-width="160px" style="padding: 10px">
|
||||
<el-divider content-position="left">招募计划</el-divider>
|
||||
<el-form-item label="开启招募">
|
||||
<el-switch v-model="form.recruitOpen" />
|
||||
</el-form-item>
|
||||
<el-form-item label="申请方式">
|
||||
<el-radio-group v-model="form.applyMode">
|
||||
<el-radio value="MANUAL_APPLY">手动申请</el-radio>
|
||||
<el-radio value="AUTO_APPLY_WHEN_QUALIFIED">满足条件自动申请</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="审核方式">
|
||||
<el-radio-group v-model="form.auditMode">
|
||||
<el-radio value="MANUAL_AUDIT">人工审核</el-radio>
|
||||
<el-radio value="AUTO_AUDIT">自动审核</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="开启加入门槛">
|
||||
<el-switch v-model="form.thresholdEnabled" />
|
||||
</el-form-item>
|
||||
<template v-if="form.thresholdEnabled">
|
||||
<el-form-item label="门槛组合">
|
||||
<el-radio-group v-model="form.thresholdMatch">
|
||||
<el-radio value="ALL">需同时满足</el-radio>
|
||||
<el-radio value="ANY">满足其一</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="最低消费金额">
|
||||
<el-input-number v-model="form.consumeAmountMin" :min="0" :precision="2" />
|
||||
</el-form-item>
|
||||
<el-form-item label="最低消费笔数">
|
||||
<el-input-number v-model="form.consumeCountMin" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="需购买指定商品">
|
||||
<el-switch v-model="form.requireBuyGoods" />
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item label="招募页标题">
|
||||
<el-input v-model="form.recruitPageTitle" />
|
||||
</el-form-item>
|
||||
<el-form-item label="招募页说明">
|
||||
<el-input v-model="form.recruitPageDesc" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="展示招募入口">
|
||||
<el-switch v-model="form.showRecruitEntry" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="submit">保存</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { setSetting, getSetting } from "@/api/index";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
defineOptions({ name: "distributionRecruitSetting" });
|
||||
|
||||
const formRef = ref(null);
|
||||
const form = ref({
|
||||
recruitOpen: true,
|
||||
applyMode: "MANUAL_APPLY",
|
||||
auditMode: "MANUAL_AUDIT",
|
||||
thresholdEnabled: false,
|
||||
thresholdMatch: "ALL",
|
||||
consumeAmountMin: 0,
|
||||
consumeCountMin: 0,
|
||||
requireBuyGoods: false,
|
||||
recruitPageTitle: "成为分销员",
|
||||
recruitPageDesc: "分享商品赚取佣金",
|
||||
showRecruitEntry: true,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
getSetting("DISTRIBUTION_RECRUIT_SETTING").then((res) => {
|
||||
if (res.success && res.result) {
|
||||
form.value = { ...form.value, ...res.result };
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function submit() {
|
||||
setSetting("DISTRIBUTION_RECRUIT_SETTING", form.value).then((res) => {
|
||||
if (res.success) Message.success("保存成功");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -4,7 +4,7 @@
|
||||
<el-tabs v-model="activeTab" class="setting-tabs">
|
||||
<el-tab-pane label="基础设置" name="base">
|
||||
<el-form
|
||||
ref="form"
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="formRule"
|
||||
label-width="180px"
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
<el-tab-pane label="客户关系" name="customer">
|
||||
<el-form
|
||||
ref="customerForm"
|
||||
ref="customerFormRef"
|
||||
:model="customerForm"
|
||||
label-width="180px"
|
||||
label-position="right"
|
||||
@@ -116,7 +116,7 @@
|
||||
|
||||
<el-tab-pane label="结算方案" name="settlement">
|
||||
<el-form
|
||||
ref="settlementForm"
|
||||
ref="settlementFormRef"
|
||||
:model="settlementForm"
|
||||
label-width="180px"
|
||||
label-position="right"
|
||||
@@ -234,7 +234,7 @@
|
||||
|
||||
<el-tab-pane label="商品分销设置" name="featured">
|
||||
<el-form
|
||||
ref="featuredForm"
|
||||
ref="featuredFormRef"
|
||||
:model="featuredForm"
|
||||
label-width="220px"
|
||||
label-position="right"
|
||||
@@ -428,7 +428,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch, onMounted, nextTick } from "vue";
|
||||
import { setSetting, getSetting } from "@/api/index";
|
||||
import {
|
||||
getDistributionCustomerSetting,
|
||||
@@ -445,6 +446,7 @@ import {
|
||||
} from "@/api/distribution";
|
||||
import uploadPicInput from "@/components/lili/upload-pic-input";
|
||||
import { regular } from "@/utils";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
const DEFAULT_FORM = {
|
||||
isOpen: true,
|
||||
@@ -485,430 +487,426 @@ const DEFAULT_SORT_OPTIONS = [
|
||||
{ value: "LATEST_LISTING", label: "商品上架越晚越靠前" },
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "distributionSetting",
|
||||
components: {
|
||||
uploadPicInput,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: "base",
|
||||
saving: {
|
||||
base: false,
|
||||
customer: false,
|
||||
settlement: false,
|
||||
featured: false,
|
||||
poster: false,
|
||||
inviteCard: false,
|
||||
},
|
||||
form: { ...DEFAULT_FORM },
|
||||
formRule: {
|
||||
isOpen: [regular.REQUIRED],
|
||||
distributorName: [regular.REQUIRED, regular.VARCHAR20],
|
||||
},
|
||||
customerForm: {
|
||||
bindMode: "SHORT_TERM",
|
||||
permanentBindMode: "PAID_WITHIN_DAYS",
|
||||
permanentPaidDays: 1,
|
||||
shortTermBindDays: 7,
|
||||
},
|
||||
settlementForm: {
|
||||
selfPurchaseCommissionMode: "SELF",
|
||||
},
|
||||
posterForm: { ...DEFAULT_POSTER_FORM },
|
||||
inviteCardForm: { ...DEFAULT_INVITE_CARD_FORM },
|
||||
featuredForm: { ...DEFAULT_FEATURED_FORM },
|
||||
defaultSortOptions: DEFAULT_SORT_OPTIONS,
|
||||
featuredCustomGoods: [],
|
||||
featuredPickerVisible: false,
|
||||
featuredPickerLoading: false,
|
||||
featuredPickerList: [],
|
||||
featuredPickerTotal: 0,
|
||||
featuredPickerDraft: [],
|
||||
featuredPickerSelection: [],
|
||||
syncingFeaturedPickerSelection: false,
|
||||
featuredPickerSearch: {
|
||||
goodsName: "",
|
||||
authStatus: "PASS",
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
defineOptions({ name: "distributionSetting" });
|
||||
|
||||
const formRef = ref(null);
|
||||
const featuredPickerTable = ref(null);
|
||||
|
||||
const activeTab = ref("base");
|
||||
const saving = reactive({
|
||||
base: false,
|
||||
customer: false,
|
||||
settlement: false,
|
||||
featured: false,
|
||||
poster: false,
|
||||
inviteCard: false,
|
||||
});
|
||||
const form = ref({ ...DEFAULT_FORM });
|
||||
const formRule = reactive({
|
||||
isOpen: [regular.REQUIRED],
|
||||
distributorName: [regular.REQUIRED, regular.VARCHAR20],
|
||||
});
|
||||
const customerForm = ref({
|
||||
bindMode: "SHORT_TERM",
|
||||
permanentBindMode: "PAID_WITHIN_DAYS",
|
||||
permanentPaidDays: 1,
|
||||
shortTermBindDays: 7,
|
||||
});
|
||||
const settlementForm = ref({
|
||||
selfPurchaseCommissionMode: "SELF",
|
||||
});
|
||||
const posterForm = ref({ ...DEFAULT_POSTER_FORM });
|
||||
const inviteCardForm = ref({ ...DEFAULT_INVITE_CARD_FORM });
|
||||
const featuredForm = ref({ ...DEFAULT_FEATURED_FORM });
|
||||
const defaultSortOptions = ref(DEFAULT_SORT_OPTIONS);
|
||||
const featuredCustomGoods = ref([]);
|
||||
const featuredPickerVisible = ref(false);
|
||||
const featuredPickerLoading = ref(false);
|
||||
const featuredPickerList = ref([]);
|
||||
const featuredPickerTotal = ref(0);
|
||||
const featuredPickerDraft = ref([]);
|
||||
const featuredPickerSelection = ref([]);
|
||||
const syncingFeaturedPickerSelection = ref(false);
|
||||
const featuredPickerSearch = reactive({
|
||||
goodsName: "",
|
||||
authStatus: "PASS",
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
|
||||
const featuredGoodsLimit = computed(() => {
|
||||
return featuredForm.value.displayCount === 10 ? 10 : 5;
|
||||
});
|
||||
const featuredGoodsOverLimit = computed(() => {
|
||||
return featuredForm.value.sourceMode === "CUSTOM" && featuredCustomGoods.value.length > featuredGoodsLimit.value;
|
||||
});
|
||||
const featuredDisplayCountDesc = computed(() => {
|
||||
if (featuredForm.value.sourceMode === "CUSTOM") {
|
||||
return `最多选择 ${featuredGoodsLimit.value} 个,分销员端按顺序展示。`;
|
||||
}
|
||||
return `分销员端展示前 ${featuredGoodsLimit.value} 个。`;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => customerForm.value.permanentBindMode,
|
||||
(value) => {
|
||||
if (value === "PAID_WITHIN_DAYS") {
|
||||
normalizePermanentPaidDays();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
function init() {
|
||||
loadBaseSetting();
|
||||
loadFeaturedSetting();
|
||||
loadPosterSetting();
|
||||
loadInviteCardSetting();
|
||||
loadCustomerSetting();
|
||||
loadSettlementSetting();
|
||||
}
|
||||
function loadBaseSetting() {
|
||||
getSetting("DISTRIBUTION_SETTING").then((res) => {
|
||||
if (!res.success || !res.result) {
|
||||
return;
|
||||
}
|
||||
const result = res.result;
|
||||
form.value = {
|
||||
...DEFAULT_FORM,
|
||||
...result,
|
||||
distributorName: result.distributorName || "分销员",
|
||||
parentAttributionMode: result.parentAttributionMode || DEFAULT_FORM.parentAttributionMode,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
featuredGoodsLimit() {
|
||||
return this.featuredForm.displayCount === 10 ? 10 : 5;
|
||||
},
|
||||
featuredGoodsOverLimit() {
|
||||
return this.featuredForm.sourceMode === "CUSTOM" && this.featuredCustomGoods.length > this.featuredGoodsLimit;
|
||||
},
|
||||
featuredDisplayCountDesc() {
|
||||
if (this.featuredForm.sourceMode === "CUSTOM") {
|
||||
return `最多选择 ${this.featuredGoodsLimit} 个,分销员端按顺序展示。`;
|
||||
});
|
||||
}
|
||||
function loadPosterSetting() {
|
||||
getDistributionPromotionPosterSetting().then((res) => {
|
||||
if (!res.success || !res.result) {
|
||||
return;
|
||||
}
|
||||
const result = res.result;
|
||||
posterForm.value = {
|
||||
...DEFAULT_POSTER_FORM,
|
||||
memberInfoVisible: result.memberInfoVisible !== false,
|
||||
backgroundImage: result.backgroundImage || "",
|
||||
slogan: result.slogan || DEFAULT_POSTER_FORM.slogan,
|
||||
textColor: result.textColor || DEFAULT_POSTER_FORM.textColor,
|
||||
};
|
||||
});
|
||||
}
|
||||
function submitPoster() {
|
||||
const slogan = (posterForm.value.slogan || "").trim();
|
||||
if (!slogan) {
|
||||
Message.warning("请填写推荐语");
|
||||
return;
|
||||
}
|
||||
saving.poster = true;
|
||||
putDistributionPromotionPosterSetting({
|
||||
memberInfoVisible: posterForm.value.memberInfoVisible,
|
||||
backgroundImage: (posterForm.value.backgroundImage || "").trim(),
|
||||
slogan,
|
||||
textColor: posterForm.value.textColor || DEFAULT_POSTER_FORM.textColor,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
loadPosterSetting();
|
||||
}
|
||||
return `分销员端展示前 ${this.featuredGoodsLimit} 个。`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
watch: {
|
||||
"customerForm.permanentBindMode"(value) {
|
||||
if (value === "PAID_WITHIN_DAYS") {
|
||||
this.normalizePermanentPaidDays();
|
||||
})
|
||||
.finally(() => {
|
||||
saving.poster = false;
|
||||
});
|
||||
}
|
||||
function loadInviteCardSetting() {
|
||||
getDistributionInviteCardSetting().then((res) => {
|
||||
if (!res.success || !res.result) {
|
||||
return;
|
||||
}
|
||||
const result = res.result;
|
||||
inviteCardForm.value = {
|
||||
...DEFAULT_INVITE_CARD_FORM,
|
||||
memberInfoVisible: result.memberInfoVisible !== false,
|
||||
backgroundImage: result.backgroundImage || "",
|
||||
slogan: result.slogan || DEFAULT_INVITE_CARD_FORM.slogan,
|
||||
textColor: result.textColor || DEFAULT_INVITE_CARD_FORM.textColor,
|
||||
};
|
||||
});
|
||||
}
|
||||
function submitInviteCard() {
|
||||
const slogan = (inviteCardForm.value.slogan || "").trim();
|
||||
if (!slogan) {
|
||||
Message.warning("请填写推荐语");
|
||||
return;
|
||||
}
|
||||
saving.inviteCard = true;
|
||||
putDistributionInviteCardSetting({
|
||||
memberInfoVisible: inviteCardForm.value.memberInfoVisible,
|
||||
backgroundImage: (inviteCardForm.value.backgroundImage || "").trim(),
|
||||
slogan,
|
||||
textColor: inviteCardForm.value.textColor || DEFAULT_INVITE_CARD_FORM.textColor,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
loadInviteCardSetting();
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.loadBaseSetting();
|
||||
this.loadFeaturedSetting();
|
||||
this.loadPosterSetting();
|
||||
this.loadInviteCardSetting();
|
||||
this.loadCustomerSetting();
|
||||
this.loadSettlementSetting();
|
||||
},
|
||||
loadBaseSetting() {
|
||||
getSetting("DISTRIBUTION_SETTING").then((res) => {
|
||||
if (!res.success || !res.result) {
|
||||
return;
|
||||
}
|
||||
const result = res.result;
|
||||
this.form = {
|
||||
...DEFAULT_FORM,
|
||||
...result,
|
||||
distributorName: result.distributorName || "分销员",
|
||||
parentAttributionMode: result.parentAttributionMode || DEFAULT_FORM.parentAttributionMode,
|
||||
};
|
||||
})
|
||||
.finally(() => {
|
||||
saving.inviteCard = false;
|
||||
});
|
||||
}
|
||||
function loadFeaturedSetting() {
|
||||
getDistributionFeaturedGoodsSetting().then((res) => {
|
||||
if (!res.success || !res.result) {
|
||||
return;
|
||||
}
|
||||
const result = res.result;
|
||||
featuredForm.value = {
|
||||
...DEFAULT_FEATURED_FORM,
|
||||
defaultSort: result.defaultSort || DEFAULT_FEATURED_FORM.defaultSort,
|
||||
defaultShareText: result.defaultShareText || "",
|
||||
centerVisible: !!result.centerVisible,
|
||||
showSales: !!result.showSales,
|
||||
sourceMode: result.sourceMode || DEFAULT_FEATURED_FORM.sourceMode,
|
||||
matchRule: result.matchRule || DEFAULT_FEATURED_FORM.matchRule,
|
||||
displayCount: result.displayCount === 10 ? 10 : 5,
|
||||
};
|
||||
featuredCustomGoods.value = Array.isArray(result.customGoods) ? [...result.customGoods] : [];
|
||||
});
|
||||
}
|
||||
function loadCustomerSetting() {
|
||||
getDistributionCustomerSetting().then((res) => {
|
||||
if (res.success && res.result) {
|
||||
customerForm.value = { ...customerForm.value, ...res.result };
|
||||
normalizePermanentPaidDays();
|
||||
}
|
||||
});
|
||||
}
|
||||
function normalizePermanentPaidDays() {
|
||||
const days = Number(customerForm.value.permanentPaidDays);
|
||||
if (!Number.isFinite(days) || days < 1) {
|
||||
customerForm.value.permanentPaidDays = 1;
|
||||
} else {
|
||||
customerForm.value.permanentPaidDays = Math.floor(days);
|
||||
}
|
||||
}
|
||||
function onPermanentPaidDaysChange(value) {
|
||||
if (value == null || value < 1) {
|
||||
customerForm.value.permanentPaidDays = 1;
|
||||
}
|
||||
}
|
||||
function loadSettlementSetting() {
|
||||
getDistributionSettlementSetting().then((res) => {
|
||||
if (res.success && res.result) {
|
||||
settlementForm.value = { ...settlementForm.value, ...res.result };
|
||||
}
|
||||
});
|
||||
}
|
||||
function openFeaturedGoodsPicker() {
|
||||
featuredPickerDraft.value = featuredCustomGoods.value.map((item) => ({ ...item }));
|
||||
featuredPickerVisible.value = true;
|
||||
}
|
||||
function loadFeaturedPickerGoods() {
|
||||
featuredPickerLoading.value = true;
|
||||
syncingFeaturedPickerSelection.value = true;
|
||||
getDistributionGoods({ ...featuredPickerSearch })
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
featuredPickerList.value = res.result?.records || [];
|
||||
featuredPickerTotal.value = res.result?.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
featuredPickerLoading.value = false;
|
||||
nextTick(() => {
|
||||
syncFeaturedPickerSelection();
|
||||
nextTick(() => {
|
||||
syncingFeaturedPickerSelection.value = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
loadPosterSetting() {
|
||||
getDistributionPromotionPosterSetting().then((res) => {
|
||||
if (!res.success || !res.result) {
|
||||
return;
|
||||
}
|
||||
const result = res.result;
|
||||
this.posterForm = {
|
||||
...DEFAULT_POSTER_FORM,
|
||||
memberInfoVisible: result.memberInfoVisible !== false,
|
||||
backgroundImage: result.backgroundImage || "",
|
||||
slogan: result.slogan || DEFAULT_POSTER_FORM.slogan,
|
||||
textColor: result.textColor || DEFAULT_POSTER_FORM.textColor,
|
||||
};
|
||||
});
|
||||
},
|
||||
submitPoster() {
|
||||
const slogan = (this.posterForm.slogan || "").trim();
|
||||
if (!slogan) {
|
||||
this.$Message.warning("请填写推荐语");
|
||||
return;
|
||||
});
|
||||
}
|
||||
function syncFeaturedPickerSelection() {
|
||||
const table = featuredPickerTable.value;
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
const selectedSkuIds = new Set(
|
||||
featuredPickerDraft.value.map((item) => item.skuId).filter(Boolean)
|
||||
);
|
||||
table.clearSelection();
|
||||
featuredPickerList.value.forEach((row) => {
|
||||
if (row.skuId && selectedSkuIds.has(row.skuId)) {
|
||||
table.toggleRowSelection(row, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
function searchFeaturedPickerGoods() {
|
||||
featuredPickerSearch.pageNumber = 1;
|
||||
loadFeaturedPickerGoods();
|
||||
}
|
||||
function onFeaturedPickerPageSizeChange() {
|
||||
featuredPickerSearch.pageNumber = 1;
|
||||
loadFeaturedPickerGoods();
|
||||
}
|
||||
function onFeaturedPickerSelectionChange(selection) {
|
||||
if (syncingFeaturedPickerSelection.value) {
|
||||
return;
|
||||
}
|
||||
const selectedSkuIds = new Set((selection || []).map((item) => item.skuId).filter(Boolean));
|
||||
const currentPageSkuIds = new Set(featuredPickerList.value.map((item) => item.skuId).filter(Boolean));
|
||||
const remaining = featuredPickerDraft.value.filter((item) => {
|
||||
if (!currentPageSkuIds.has(item.skuId)) {
|
||||
return true;
|
||||
}
|
||||
return selectedSkuIds.has(item.skuId);
|
||||
});
|
||||
const remainingSkuIds = new Set(remaining.map((item) => item.skuId));
|
||||
const added = [];
|
||||
(selection || []).forEach((item) => {
|
||||
if (!item.skuId || remainingSkuIds.has(item.skuId)) {
|
||||
return;
|
||||
}
|
||||
remainingSkuIds.add(item.skuId);
|
||||
added.push(toFeaturedGoodsItem(item));
|
||||
});
|
||||
featuredPickerDraft.value = [...remaining, ...added];
|
||||
featuredPickerSelection.value = selection || [];
|
||||
}
|
||||
function toFeaturedGoodsItem(item) {
|
||||
return {
|
||||
id: item.id,
|
||||
goodsId: item.goodsId,
|
||||
skuId: item.skuId,
|
||||
goodsName: item.goodsName,
|
||||
specs: item.specs,
|
||||
thumbnail: item.thumbnail,
|
||||
price: item.price,
|
||||
commission: item.commission,
|
||||
storeName: item.storeName,
|
||||
};
|
||||
}
|
||||
function featuredGoodsLimitMessage() {
|
||||
const limit = featuredGoodsLimit.value;
|
||||
return `主推商品数量为${limit}时,最多选择${limit}个自定义商品`;
|
||||
}
|
||||
function confirmFeaturedPicker() {
|
||||
const next = featuredPickerDraft.value.filter((item) => item.skuId);
|
||||
if (!next.length) {
|
||||
Message.warning("请至少选择一个分销商品");
|
||||
return;
|
||||
}
|
||||
const limit = featuredGoodsLimit.value;
|
||||
if (next.length > limit) {
|
||||
Message.warning(featuredGoodsLimitMessage());
|
||||
return;
|
||||
}
|
||||
featuredCustomGoods.value = next;
|
||||
featuredPickerVisible.value = false;
|
||||
featuredPickerSelection.value = [];
|
||||
}
|
||||
function removeFeaturedGoods(index) {
|
||||
featuredCustomGoods.value.splice(index, 1);
|
||||
}
|
||||
function buildFeaturedPayload() {
|
||||
return {
|
||||
defaultSort: featuredForm.value.defaultSort,
|
||||
defaultShareText: (featuredForm.value.defaultShareText || "").trim(),
|
||||
centerVisible: featuredForm.value.centerVisible,
|
||||
showSales: featuredForm.value.showSales,
|
||||
sourceMode: featuredForm.value.sourceMode,
|
||||
matchRule: featuredForm.value.matchRule,
|
||||
displayCount: featuredForm.value.displayCount === 10 ? 10 : 5,
|
||||
skuIds:
|
||||
featuredForm.value.sourceMode === "CUSTOM"
|
||||
? featuredCustomGoods.value.map((item) => item.skuId).filter(Boolean)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
function submitFeatured() {
|
||||
if (featuredForm.value.sourceMode === "CUSTOM" && !featuredCustomGoods.value.length) {
|
||||
Message.warning("请至少选择一个自定义主推商品");
|
||||
return;
|
||||
}
|
||||
if (featuredGoodsOverLimit.value) {
|
||||
Message.warning(featuredGoodsLimitMessage());
|
||||
return;
|
||||
}
|
||||
saving.featured = true;
|
||||
putDistributionFeaturedGoodsSetting(buildFeaturedPayload())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
loadFeaturedSetting();
|
||||
}
|
||||
this.saving.poster = true;
|
||||
putDistributionPromotionPosterSetting({
|
||||
memberInfoVisible: this.posterForm.memberInfoVisible,
|
||||
backgroundImage: (this.posterForm.backgroundImage || "").trim(),
|
||||
slogan,
|
||||
textColor: this.posterForm.textColor || DEFAULT_POSTER_FORM.textColor,
|
||||
})
|
||||
.finally(() => {
|
||||
saving.featured = false;
|
||||
});
|
||||
}
|
||||
function buildBasePayload() {
|
||||
return {
|
||||
isOpen: form.value.isOpen,
|
||||
distributorName: (form.value.distributorName || "分销员").trim(),
|
||||
upgradeConditionVisible: form.value.upgradeConditionVisible,
|
||||
parentDistributorVisible: form.value.parentDistributorVisible,
|
||||
parentAttributionMode: form.value.parentAttributionMode,
|
||||
};
|
||||
}
|
||||
function submitBase() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
saving.base = true;
|
||||
setSetting("DISTRIBUTION_SETTING", buildBasePayload())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
loadBaseSetting();
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.loadPosterSetting();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving.poster = false;
|
||||
});
|
||||
},
|
||||
loadInviteCardSetting() {
|
||||
getDistributionInviteCardSetting().then((res) => {
|
||||
if (!res.success || !res.result) {
|
||||
return;
|
||||
}
|
||||
const result = res.result;
|
||||
this.inviteCardForm = {
|
||||
...DEFAULT_INVITE_CARD_FORM,
|
||||
memberInfoVisible: result.memberInfoVisible !== false,
|
||||
backgroundImage: result.backgroundImage || "",
|
||||
slogan: result.slogan || DEFAULT_INVITE_CARD_FORM.slogan,
|
||||
textColor: result.textColor || DEFAULT_INVITE_CARD_FORM.textColor,
|
||||
};
|
||||
.finally(() => {
|
||||
saving.base = false;
|
||||
});
|
||||
},
|
||||
submitInviteCard() {
|
||||
const slogan = (this.inviteCardForm.slogan || "").trim();
|
||||
if (!slogan) {
|
||||
this.$Message.warning("请填写推荐语");
|
||||
return;
|
||||
});
|
||||
}
|
||||
function submitCustomer() {
|
||||
normalizePermanentPaidDays();
|
||||
if (
|
||||
customerForm.value.bindMode === "PERMANENT" &&
|
||||
customerForm.value.permanentBindMode === "PAID_WITHIN_DAYS" &&
|
||||
customerForm.value.permanentPaidDays < 1
|
||||
) {
|
||||
Message.warning("永久锁客支付天数不能低于1天");
|
||||
return;
|
||||
}
|
||||
saving.customer = true;
|
||||
putDistributionCustomerSetting(customerForm.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
loadCustomerSetting();
|
||||
}
|
||||
this.saving.inviteCard = true;
|
||||
putDistributionInviteCardSetting({
|
||||
memberInfoVisible: this.inviteCardForm.memberInfoVisible,
|
||||
backgroundImage: (this.inviteCardForm.backgroundImage || "").trim(),
|
||||
slogan,
|
||||
textColor: this.inviteCardForm.textColor || DEFAULT_INVITE_CARD_FORM.textColor,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.loadInviteCardSetting();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving.inviteCard = false;
|
||||
});
|
||||
},
|
||||
loadFeaturedSetting() {
|
||||
getDistributionFeaturedGoodsSetting().then((res) => {
|
||||
if (!res.success || !res.result) {
|
||||
return;
|
||||
}
|
||||
const result = res.result;
|
||||
this.featuredForm = {
|
||||
...DEFAULT_FEATURED_FORM,
|
||||
defaultSort: result.defaultSort || DEFAULT_FEATURED_FORM.defaultSort,
|
||||
defaultShareText: result.defaultShareText || "",
|
||||
centerVisible: !!result.centerVisible,
|
||||
showSales: !!result.showSales,
|
||||
sourceMode: result.sourceMode || DEFAULT_FEATURED_FORM.sourceMode,
|
||||
matchRule: result.matchRule || DEFAULT_FEATURED_FORM.matchRule,
|
||||
displayCount: result.displayCount === 10 ? 10 : 5,
|
||||
};
|
||||
this.featuredCustomGoods = Array.isArray(result.customGoods) ? [...result.customGoods] : [];
|
||||
});
|
||||
},
|
||||
loadCustomerSetting() {
|
||||
getDistributionCustomerSetting().then((res) => {
|
||||
if (res.success && res.result) {
|
||||
this.customerForm = { ...this.customerForm, ...res.result };
|
||||
this.normalizePermanentPaidDays();
|
||||
}
|
||||
});
|
||||
},
|
||||
normalizePermanentPaidDays() {
|
||||
const days = Number(this.customerForm.permanentPaidDays);
|
||||
if (!Number.isFinite(days) || days < 1) {
|
||||
this.customerForm.permanentPaidDays = 1;
|
||||
} else {
|
||||
this.customerForm.permanentPaidDays = Math.floor(days);
|
||||
})
|
||||
.finally(() => {
|
||||
saving.customer = false;
|
||||
});
|
||||
}
|
||||
function submitSettlement() {
|
||||
saving.settlement = true;
|
||||
putDistributionSettlementSetting(settlementForm.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
loadSettlementSetting();
|
||||
}
|
||||
},
|
||||
onPermanentPaidDaysChange(value) {
|
||||
if (value == null || value < 1) {
|
||||
this.customerForm.permanentPaidDays = 1;
|
||||
}
|
||||
},
|
||||
loadSettlementSetting() {
|
||||
getDistributionSettlementSetting().then((res) => {
|
||||
if (res.success && res.result) {
|
||||
this.settlementForm = { ...this.settlementForm, ...res.result };
|
||||
}
|
||||
});
|
||||
},
|
||||
openFeaturedGoodsPicker() {
|
||||
this.featuredPickerDraft = this.featuredCustomGoods.map((item) => ({ ...item }));
|
||||
this.featuredPickerVisible = true;
|
||||
},
|
||||
loadFeaturedPickerGoods() {
|
||||
this.featuredPickerLoading = true;
|
||||
this.syncingFeaturedPickerSelection = true;
|
||||
getDistributionGoods({ ...this.featuredPickerSearch })
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.featuredPickerList = res.result?.records || [];
|
||||
this.featuredPickerTotal = res.result?.total || 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.featuredPickerLoading = false;
|
||||
this.$nextTick(() => {
|
||||
this.syncFeaturedPickerSelection();
|
||||
this.$nextTick(() => {
|
||||
this.syncingFeaturedPickerSelection = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
syncFeaturedPickerSelection() {
|
||||
const table = this.$refs.featuredPickerTable;
|
||||
if (!table) {
|
||||
return;
|
||||
}
|
||||
const selectedSkuIds = new Set(
|
||||
this.featuredPickerDraft.map((item) => item.skuId).filter(Boolean)
|
||||
);
|
||||
table.clearSelection();
|
||||
this.featuredPickerList.forEach((row) => {
|
||||
if (row.skuId && selectedSkuIds.has(row.skuId)) {
|
||||
table.toggleRowSelection(row, true);
|
||||
}
|
||||
});
|
||||
},
|
||||
searchFeaturedPickerGoods() {
|
||||
this.featuredPickerSearch.pageNumber = 1;
|
||||
this.loadFeaturedPickerGoods();
|
||||
},
|
||||
onFeaturedPickerPageSizeChange() {
|
||||
this.featuredPickerSearch.pageNumber = 1;
|
||||
this.loadFeaturedPickerGoods();
|
||||
},
|
||||
onFeaturedPickerSelectionChange(selection) {
|
||||
if (this.syncingFeaturedPickerSelection) {
|
||||
return;
|
||||
}
|
||||
const selectedSkuIds = new Set((selection || []).map((item) => item.skuId).filter(Boolean));
|
||||
const currentPageSkuIds = new Set(this.featuredPickerList.map((item) => item.skuId).filter(Boolean));
|
||||
const remaining = this.featuredPickerDraft.filter((item) => {
|
||||
if (!currentPageSkuIds.has(item.skuId)) {
|
||||
return true;
|
||||
}
|
||||
return selectedSkuIds.has(item.skuId);
|
||||
});
|
||||
const remainingSkuIds = new Set(remaining.map((item) => item.skuId));
|
||||
const added = [];
|
||||
(selection || []).forEach((item) => {
|
||||
if (!item.skuId || remainingSkuIds.has(item.skuId)) {
|
||||
return;
|
||||
}
|
||||
remainingSkuIds.add(item.skuId);
|
||||
added.push(this.toFeaturedGoodsItem(item));
|
||||
});
|
||||
this.featuredPickerDraft = [...remaining, ...added];
|
||||
this.featuredPickerSelection = selection || [];
|
||||
},
|
||||
toFeaturedGoodsItem(item) {
|
||||
return {
|
||||
id: item.id,
|
||||
goodsId: item.goodsId,
|
||||
skuId: item.skuId,
|
||||
goodsName: item.goodsName,
|
||||
specs: item.specs,
|
||||
thumbnail: item.thumbnail,
|
||||
price: item.price,
|
||||
commission: item.commission,
|
||||
storeName: item.storeName,
|
||||
};
|
||||
},
|
||||
featuredGoodsLimitMessage() {
|
||||
const limit = this.featuredGoodsLimit;
|
||||
return `主推商品数量为${limit}时,最多选择${limit}个自定义商品`;
|
||||
},
|
||||
confirmFeaturedPicker() {
|
||||
const next = this.featuredPickerDraft.filter((item) => item.skuId);
|
||||
if (!next.length) {
|
||||
this.$Message.warning("请至少选择一个分销商品");
|
||||
return;
|
||||
}
|
||||
const limit = this.featuredGoodsLimit;
|
||||
if (next.length > limit) {
|
||||
this.$Message.warning(this.featuredGoodsLimitMessage());
|
||||
return;
|
||||
}
|
||||
this.featuredCustomGoods = next;
|
||||
this.featuredPickerVisible = false;
|
||||
this.featuredPickerSelection = [];
|
||||
},
|
||||
removeFeaturedGoods(index) {
|
||||
this.featuredCustomGoods.splice(index, 1);
|
||||
},
|
||||
buildFeaturedPayload() {
|
||||
return {
|
||||
defaultSort: this.featuredForm.defaultSort,
|
||||
defaultShareText: (this.featuredForm.defaultShareText || "").trim(),
|
||||
centerVisible: this.featuredForm.centerVisible,
|
||||
showSales: this.featuredForm.showSales,
|
||||
sourceMode: this.featuredForm.sourceMode,
|
||||
matchRule: this.featuredForm.matchRule,
|
||||
displayCount: this.featuredForm.displayCount === 10 ? 10 : 5,
|
||||
skuIds:
|
||||
this.featuredForm.sourceMode === "CUSTOM"
|
||||
? this.featuredCustomGoods.map((item) => item.skuId).filter(Boolean)
|
||||
: undefined,
|
||||
};
|
||||
},
|
||||
submitFeatured() {
|
||||
if (this.featuredForm.sourceMode === "CUSTOM" && !this.featuredCustomGoods.length) {
|
||||
this.$Message.warning("请至少选择一个自定义主推商品");
|
||||
return;
|
||||
}
|
||||
if (this.featuredGoodsOverLimit) {
|
||||
this.$Message.warning(this.featuredGoodsLimitMessage());
|
||||
return;
|
||||
}
|
||||
this.saving.featured = true;
|
||||
putDistributionFeaturedGoodsSetting(this.buildFeaturedPayload())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.loadFeaturedSetting();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving.featured = false;
|
||||
});
|
||||
},
|
||||
buildBasePayload() {
|
||||
return {
|
||||
isOpen: this.form.isOpen,
|
||||
distributorName: (this.form.distributorName || "分销员").trim(),
|
||||
upgradeConditionVisible: this.form.upgradeConditionVisible,
|
||||
parentDistributorVisible: this.form.parentDistributorVisible,
|
||||
parentAttributionMode: this.form.parentAttributionMode,
|
||||
};
|
||||
},
|
||||
submitBase() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.saving.base = true;
|
||||
setSetting("DISTRIBUTION_SETTING", this.buildBasePayload())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.loadBaseSetting();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving.base = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
submitCustomer() {
|
||||
this.normalizePermanentPaidDays();
|
||||
if (
|
||||
this.customerForm.bindMode === "PERMANENT" &&
|
||||
this.customerForm.permanentBindMode === "PAID_WITHIN_DAYS" &&
|
||||
this.customerForm.permanentPaidDays < 1
|
||||
) {
|
||||
this.$Message.warning("永久锁客支付天数不能低于1天");
|
||||
return;
|
||||
}
|
||||
this.saving.customer = true;
|
||||
putDistributionCustomerSetting(this.customerForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.loadCustomerSetting();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving.customer = false;
|
||||
});
|
||||
},
|
||||
submitSettlement() {
|
||||
this.saving.settlement = true;
|
||||
putDistributionSettlementSetting(this.settlementForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.loadSettlementSetting();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving.settlement = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.finally(() => {
|
||||
saving.settlement = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -17,20 +17,21 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Error403",
|
||||
methods: {
|
||||
backPage() {
|
||||
this.$router.go(-1);
|
||||
},
|
||||
goHome() {
|
||||
this.$router.push({
|
||||
name: "home_index",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
defineOptions({ name: "Error403" });
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function backPage() {
|
||||
router.go(-1);
|
||||
}
|
||||
function goHome() {
|
||||
router.push({
|
||||
name: "home_index",
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -17,20 +17,22 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Error404",
|
||||
methods: {
|
||||
backPage() {
|
||||
this.$router.go(-1);
|
||||
},
|
||||
goHome() {
|
||||
this.$router.push({
|
||||
name: "home_index",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
defineOptions({ name: "Error404" });
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function backPage() {
|
||||
router.go(-1);
|
||||
}
|
||||
|
||||
function goHome() {
|
||||
router.push({
|
||||
name: "home_index",
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -17,20 +17,22 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Error500",
|
||||
methods: {
|
||||
backPage() {
|
||||
this.$router.go(-1);
|
||||
},
|
||||
goHome() {
|
||||
this.$router.push({
|
||||
name: "home_index",
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
defineOptions({ name: "Error500" });
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
function backPage() {
|
||||
router.go(-1);
|
||||
}
|
||||
|
||||
function goHome() {
|
||||
router.push({
|
||||
name: "home_index",
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -4,22 +4,23 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ExternalLink",
|
||||
data() {
|
||||
return {
|
||||
link: this.$router.currentRoute.name,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
let iframe = document.getElementById("ifram");
|
||||
const deviceWidth = document.body.clientWidth;
|
||||
const deviceHeight = document.body.clientHeight;
|
||||
iframe.style.width = deviceWidth + "px";
|
||||
iframe.style.height = deviceHeight + "px";
|
||||
}
|
||||
};
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
defineOptions({ name: "ExternalLink" });
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const link = ref(router.currentRoute.name);
|
||||
|
||||
onMounted(() => {
|
||||
let iframe = document.getElementById("ifram");
|
||||
const deviceWidth = document.body.clientWidth;
|
||||
const deviceHeight = document.body.clientHeight;
|
||||
iframe.style.width = deviceWidth + "px";
|
||||
iframe.style.height = deviceHeight + "px";
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
@@ -24,39 +24,39 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import * as API_Finance from "@/api/finance";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
|
||||
export default {
|
||||
name: "finance-payment-method",
|
||||
data() {
|
||||
return { loading: false, dateRange: [], data: [] };
|
||||
},
|
||||
mounted() {
|
||||
this.loadData();
|
||||
},
|
||||
methods: {
|
||||
buildParams() {
|
||||
const p = {};
|
||||
if (this.dateRange?.length === 2) {
|
||||
p.startDate = this.dateRange[0];
|
||||
p.endDate = this.dateRange[1];
|
||||
}
|
||||
return p;
|
||||
},
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
API_Finance.getPaymentMethodReport(this.buildParams()).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) this.data = res.result || [];
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
API_Finance.exportPaymentMethodReport(this.buildParams()).then((blob) => {
|
||||
downloadBlob(blob, "支付方式汇总.xlsx");
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "finance-payment-method" });
|
||||
|
||||
const loading = ref(false);
|
||||
const dateRange = ref([]);
|
||||
const data = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
function buildParams() {
|
||||
const p = {};
|
||||
if (dateRange.value?.length === 2) {
|
||||
p.startDate = dateRange.value[0];
|
||||
p.endDate = dateRange.value[1];
|
||||
}
|
||||
return p;
|
||||
}
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
API_Finance.getPaymentMethodReport(buildParams()).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) data.value = res.result || [];
|
||||
});
|
||||
}
|
||||
function handleExport() {
|
||||
API_Finance.exportPaymentMethodReport(buildParams()).then((blob) => {
|
||||
downloadBlob(blob, "支付方式汇总.xlsx");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -39,44 +39,40 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import * as API_Finance from "@/api/finance";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
|
||||
export default {
|
||||
name: "finance-platform-report",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
dateRange: [],
|
||||
params: { granularity: "DAY" },
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadData();
|
||||
},
|
||||
methods: {
|
||||
buildParams() {
|
||||
const p = { ...this.params };
|
||||
if (this.dateRange && this.dateRange.length === 2) {
|
||||
p.startDate = this.dateRange[0];
|
||||
p.endDate = this.dateRange[1];
|
||||
}
|
||||
return p;
|
||||
},
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
API_Finance.getPlatformReport(this.buildParams()).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) this.data = res.result || [];
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
API_Finance.exportPlatformReport(this.buildParams()).then((blob) => {
|
||||
downloadBlob(blob, "平台经营报表.xlsx");
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "finance-platform-report" });
|
||||
|
||||
const loading = ref(false);
|
||||
const dateRange = ref([]);
|
||||
const params = ref({ granularity: "DAY" });
|
||||
const data = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
function buildParams() {
|
||||
const p = { ...params.value };
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
p.startDate = dateRange.value[0];
|
||||
p.endDate = dateRange.value[1];
|
||||
}
|
||||
return p;
|
||||
}
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
API_Finance.getPlatformReport(buildParams()).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) data.value = res.result || [];
|
||||
});
|
||||
}
|
||||
function handleExport() {
|
||||
API_Finance.exportPlatformReport(buildParams()).then((blob) => {
|
||||
downloadBlob(blob, "平台经营报表.xlsx");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -34,44 +34,40 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Finance from "@/api/finance";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
|
||||
export default {
|
||||
name: "finance-refund-log",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
searchForm: { pageNumber: 1, pageSize: 20, orderSn: "" },
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
API_Finance.refundLogPage(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
const { pageNumber, pageSize, ...rest } = this.searchForm;
|
||||
API_Finance.exportRefundFlow(rest).then((blob) => {
|
||||
downloadBlob(blob, "退款流水.xlsx");
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "finance-refund-log" });
|
||||
|
||||
const loading = ref(false);
|
||||
const searchForm = reactive({ pageNumber: 1, pageSize: 20, orderSn: "" });
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Finance.refundLogPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleExport() {
|
||||
const { pageNumber, pageSize, ...rest } = searchForm;
|
||||
API_Finance.exportRefundFlow(rest).then((blob) => {
|
||||
downloadBlob(blob, "退款流水.xlsx");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -16,31 +16,30 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import * as API_Finance from "@/api/finance";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
|
||||
export default {
|
||||
name: "finance-settlement-ledger",
|
||||
data() {
|
||||
return { loading: false, data: [] };
|
||||
},
|
||||
mounted() {
|
||||
this.loadData();
|
||||
},
|
||||
methods: {
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
API_Finance.getSettlementLedger({}).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) this.data = res.result || [];
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
API_Finance.exportSettlementLedger({}).then((blob) => {
|
||||
downloadBlob(blob, "结算台账.xlsx");
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "finance-settlement-ledger" });
|
||||
|
||||
const loading = ref(false);
|
||||
const data = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
API_Finance.getSettlementLedger({}).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) data.value = res.result || [];
|
||||
});
|
||||
}
|
||||
function handleExport() {
|
||||
API_Finance.exportSettlementLedger({}).then((blob) => {
|
||||
downloadBlob(blob, "结算台账.xlsx");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -26,39 +26,39 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import * as API_Finance from "@/api/finance";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
|
||||
export default {
|
||||
name: "finance-store-settlement",
|
||||
data() {
|
||||
return { loading: false, dateRange: [], data: [] };
|
||||
},
|
||||
mounted() {
|
||||
this.loadData();
|
||||
},
|
||||
methods: {
|
||||
buildParams() {
|
||||
const p = {};
|
||||
if (this.dateRange?.length === 2) {
|
||||
p.startDate = this.dateRange[0];
|
||||
p.endDate = this.dateRange[1];
|
||||
}
|
||||
return p;
|
||||
},
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
API_Finance.getStoreSettlementReport(this.buildParams()).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) this.data = res.result || [];
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
API_Finance.exportStoreSettlementReport(this.buildParams()).then((blob) => {
|
||||
downloadBlob(blob, "店铺结算汇总.xlsx");
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "finance-store-settlement" });
|
||||
|
||||
const loading = ref(false);
|
||||
const dateRange = ref([]);
|
||||
const data = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
function buildParams() {
|
||||
const p = {};
|
||||
if (dateRange.value?.length === 2) {
|
||||
p.startDate = dateRange.value[0];
|
||||
p.endDate = dateRange.value[1];
|
||||
}
|
||||
return p;
|
||||
}
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
API_Finance.getStoreSettlementReport(buildParams()).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) data.value = res.result || [];
|
||||
});
|
||||
}
|
||||
function handleExport() {
|
||||
API_Finance.exportStoreSettlementReport(buildParams()).then((blob) => {
|
||||
downloadBlob(blob, "店铺结算汇总.xlsx");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -36,59 +36,55 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Finance from "@/api/finance";
|
||||
import { getRequest } from "@/libs/axios";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
|
||||
export default {
|
||||
name: "finance-wallet-log",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
searchForm: { memberName: "" },
|
||||
dateRange: [],
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
methods: {
|
||||
handleSearch() {
|
||||
this.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
const params = {
|
||||
pageNumber: this.pageNumber,
|
||||
pageSize: this.pageSize,
|
||||
memberName: this.searchForm.memberName,
|
||||
};
|
||||
getRequest("/wallet/log", params).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
}).catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleExport() {
|
||||
const params = { memberName: this.searchForm.memberName };
|
||||
if (this.dateRange?.length === 2) {
|
||||
params.startDate = this.dateRange[0];
|
||||
params.endDate = this.dateRange[1];
|
||||
}
|
||||
API_Finance.exportWalletLog(params).then((blob) => {
|
||||
downloadBlob(blob, "钱包流水.xlsx");
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "finance-wallet-log" });
|
||||
|
||||
const loading = ref(false);
|
||||
const searchForm = reactive({ memberName: "" });
|
||||
const dateRange = ref([]);
|
||||
const pageNumber = ref(1);
|
||||
const pageSize = ref(20);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function handleSearch() {
|
||||
pageNumber.value = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
const params = {
|
||||
pageNumber: pageNumber.value,
|
||||
pageSize: pageSize.value,
|
||||
memberName: searchForm.memberName,
|
||||
};
|
||||
getRequest("/wallet/log", params).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
}).catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function handleExport() {
|
||||
const params = { memberName: searchForm.memberName };
|
||||
if (dateRange.value?.length === 2) {
|
||||
params.startDate = dateRange.value[0];
|
||||
params.endDate = dateRange.value[1];
|
||||
}
|
||||
API_Finance.exportWalletLog(params).then((blob) => {
|
||||
downloadBlob(blob, "钱包流水.xlsx");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form ref="formRef" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form-item label="计量单位" prop="name">
|
||||
<el-input v-model="form.name" clearable style="width: 100%" />
|
||||
</el-form-item>
|
||||
@@ -65,7 +65,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import {
|
||||
addGoodsUnit,
|
||||
getGoodsUnitPage,
|
||||
@@ -73,161 +74,163 @@ import {
|
||||
delGoodsUnit,
|
||||
} from "@/api/index";
|
||||
import { regular } from "@/utils";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
|
||||
export default {
|
||||
name: "goods-unit",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
modalVisible: false,
|
||||
modalTitle: "",
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
name: "",
|
||||
},
|
||||
form: {
|
||||
name: "",
|
||||
},
|
||||
formValidate: {
|
||||
name: [regular.REQUIRED, regular.VARCHAR5],
|
||||
},
|
||||
submitLoading: false,
|
||||
selectList: [],
|
||||
selectCount: 0,
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
this.clearSelectAll();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
clearSelectAll() {
|
||||
this.$refs.table?.clearSelection();
|
||||
},
|
||||
changeSelect(e) {
|
||||
this.selectList = e;
|
||||
this.selectCount = e.length;
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getGoodsUnitPage(this.searchForm)
|
||||
.then((res) => {
|
||||
defineOptions({ name: "goods-unit" });
|
||||
|
||||
const loading = ref(true);
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref("");
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
name: "",
|
||||
});
|
||||
const form = ref({
|
||||
name: "",
|
||||
});
|
||||
const formValidate = reactive({
|
||||
name: [regular.REQUIRED, regular.VARCHAR5],
|
||||
});
|
||||
const submitLoading = ref(false);
|
||||
const selectList = ref([]);
|
||||
const selectCount = ref(0);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
const table = ref(null);
|
||||
const formRef = ref(null);
|
||||
|
||||
// 未在 data 中声明、原代码直接挂载到 this 上的字段
|
||||
let id = null;
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
clearSelectAll();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function clearSelectAll() {
|
||||
table.value?.clearSelection();
|
||||
}
|
||||
function changeSelect(e) {
|
||||
selectList.value = e;
|
||||
selectCount.value = e.length;
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getGoodsUnitPage(searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function handleSubmit() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true;
|
||||
if (modalTitle.value == "添加") {
|
||||
if (data.value.find((item) => item.name == form.value.name)) {
|
||||
Message.error("请勿添加重复计量单位!");
|
||||
submitLoading.value = false;
|
||||
return;
|
||||
}
|
||||
delete form.value.id;
|
||||
addGoodsUnit(form.value).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.submitLoading = true;
|
||||
if (this.modalTitle == "添加") {
|
||||
if (this.data.find((item) => item.name == this.form.name)) {
|
||||
this.$Message.error("请勿添加重复计量单位!");
|
||||
this.submitLoading = false;
|
||||
return;
|
||||
}
|
||||
delete this.form.id;
|
||||
addGoodsUnit(this.form).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateGoodsUnit(this.id, this.form).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateGoodsUnit(id, form.value).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function add() {
|
||||
modalTitle.value = "添加";
|
||||
form.value = {};
|
||||
formRef.value?.resetFields();
|
||||
modalVisible.value = true;
|
||||
}
|
||||
function edit(v) {
|
||||
id = v.id;
|
||||
modalTitle.value = "修改";
|
||||
modalVisible.value = true;
|
||||
form.value.name = v.name;
|
||||
}
|
||||
function remove(v) {
|
||||
Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除 " + v.name + " ?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
delGoodsUnit(v.id).then((res) => {
|
||||
Modal.remove();
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
add() {
|
||||
this.modalTitle = "添加";
|
||||
this.form = {};
|
||||
this.$refs.form?.resetFields();
|
||||
this.modalVisible = true;
|
||||
},
|
||||
edit(v) {
|
||||
this.id = v.id;
|
||||
this.modalTitle = "修改";
|
||||
this.modalVisible = true;
|
||||
this.form.name = v.name;
|
||||
},
|
||||
remove(v) {
|
||||
this.$Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除 " + v.name + " ?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
delGoodsUnit(v.id).then((res) => {
|
||||
this.$Modal.remove();
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
function delAll() {
|
||||
if (selectCount.value <= 0) {
|
||||
Message.warning("您还未选择要删除的数据");
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除所选的 " + selectCount.value + " 条数据?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
let ids = "";
|
||||
selectList.value.forEach(function (e) {
|
||||
ids += e.id + ",";
|
||||
});
|
||||
ids = ids.substring(0, ids.length - 1);
|
||||
delGoodsUnit(ids).then((res) => {
|
||||
Modal.remove();
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
clearSelectAll();
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
delAll() {
|
||||
if (this.selectCount <= 0) {
|
||||
this.$Message.warning("您还未选择要删除的数据");
|
||||
return;
|
||||
}
|
||||
this.$Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除所选的 " + this.selectCount + " 条数据?",
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
let ids = "";
|
||||
this.selectList.forEach(function (e) {
|
||||
ids += e.id + ",";
|
||||
});
|
||||
ids = ids.substring(0, ids.length - 1);
|
||||
delGoodsUnit(ids).then((res) => {
|
||||
this.$Modal.remove();
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.clearSelectAll();
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -216,7 +216,7 @@
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="modalVisible" title="下架操作" width="500px" :close-on-click-modal="false">
|
||||
<el-form ref="underForm" :model="underForm" label-width="100px">
|
||||
<el-form ref="underFormRef" :model="underForm" label-width="100px">
|
||||
<el-form-item label="下架原因" prop="reason">
|
||||
<el-input v-model="underForm.reason" clearable />
|
||||
</el-form-item>
|
||||
@@ -255,7 +255,7 @@
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="batchAuditForm" :model="batchAuditForm" label-width="100px">
|
||||
<el-form ref="batchAuditFormRef" :model="batchAuditForm" label-width="100px">
|
||||
<el-form-item label="审核结果" prop="auth_flag">
|
||||
<el-radio-group v-model="batchAuditForm.auth_flag">
|
||||
<el-radio :value="1">审核通过</el-radio>
|
||||
@@ -278,7 +278,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="goodsGroupFlag" title="批量设置商品分组" width="420px">
|
||||
<el-form ref="goodsGroupForm" :model="goodsGroupForm" :rules="goodsGroupRule" label-width="90px">
|
||||
<el-form ref="goodsGroupFormRef" :model="goodsGroupForm" :rules="goodsGroupRule" label-width="90px">
|
||||
<el-form-item label="商品分组" prop="groupId">
|
||||
<el-select v-model="goodsGroupForm.groupId" clearable filterable style="width: 240px">
|
||||
<el-option
|
||||
@@ -298,7 +298,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, reactive, ref } from "vue";
|
||||
import {
|
||||
getGoodsListData,
|
||||
getGoodsNumerData,
|
||||
@@ -310,421 +311,423 @@ import {
|
||||
} from "@/api/goods";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { unitPrice, customRouterPush } from "@/utils/filters";
|
||||
export default {
|
||||
name: "goods",
|
||||
data() {
|
||||
return {
|
||||
id: "", //要操作的id
|
||||
loading: true, // 表单加载状态
|
||||
modalVisible: false, // 添加或编辑显示
|
||||
searchForm: {
|
||||
// 搜索框初始化对象
|
||||
pageNumber: 1, // 当前页数
|
||||
pageSize: 20, // 页面大小
|
||||
sort: "create_time", // 默认排序字段
|
||||
order: "desc", // 默认排序方式
|
||||
groupId: "",
|
||||
},
|
||||
underForm: {
|
||||
// 下架原因
|
||||
reason: "",
|
||||
},
|
||||
goodsAuditForm: {
|
||||
// 商品审核表单
|
||||
auth_flag: 1,
|
||||
},
|
||||
auditModalVisible: false, // 审核弹框显示状态
|
||||
auditSubmitLoading: false,
|
||||
currentAuditGoods: null, // 当前审核的商品
|
||||
submitLoading: false, // 添加或编辑提交状态
|
||||
data: [], // 表单数据
|
||||
total: 0, // 表单数据总数
|
||||
currentStatus: "ALL",
|
||||
goodsNumerData: {},
|
||||
selectedRows: [], // 选中的行数据
|
||||
selectAll: false, // 全选状态
|
||||
batchAuditModalVisible: false, // 批量审核弹框显示状态
|
||||
batchAuditSubmitLoading: false,
|
||||
batchAuditForm: {
|
||||
auth_flag: 1,
|
||||
reason: ''
|
||||
},
|
||||
goodsGroupFlag: false,
|
||||
goodsGroupLoading: false,
|
||||
goodsGroupList: [],
|
||||
goodsGroupForm: {
|
||||
groupId: ""
|
||||
},
|
||||
goodsGroupRule: {
|
||||
groupId: [{ required: true, message: "请选择商品分组", trigger: "change" }]
|
||||
}
|
||||
|
||||
defineOptions({ name: "goods" });
|
||||
|
||||
// 模板 ref(searchFormRef/underFormRef/batchAuditFormRef/goodsGroupFormRef 因与 data 同名改为 xxxRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
const underFormRef = ref(null);
|
||||
const auditForm = ref(null);
|
||||
const batchAuditFormRef = ref(null);
|
||||
const goodsGroupFormRef = ref(null);
|
||||
|
||||
const id = ref(""); //要操作的id
|
||||
const loading = ref(true); // 表单加载状态
|
||||
const modalVisible = ref(false); // 添加或编辑显示
|
||||
const searchForm = reactive({
|
||||
// 搜索框初始化对象
|
||||
pageNumber: 1, // 当前页数
|
||||
pageSize: 20, // 页面大小
|
||||
sort: "create_time", // 默认排序字段
|
||||
order: "desc", // 默认排序方式
|
||||
groupId: "",
|
||||
});
|
||||
const underForm = reactive({
|
||||
// 下架原因
|
||||
reason: "",
|
||||
});
|
||||
const goodsAuditForm = reactive({
|
||||
// 商品审核表单
|
||||
auth_flag: 1,
|
||||
});
|
||||
const auditModalVisible = ref(false); // 审核弹框显示状态
|
||||
const auditSubmitLoading = ref(false);
|
||||
const currentAuditGoods = ref(null); // 当前审核的商品
|
||||
const submitLoading = ref(false); // 添加或编辑提交状态
|
||||
const data = ref([]); // 表单数据
|
||||
const total = ref(0); // 表单数据总数
|
||||
const currentStatus = ref("ALL");
|
||||
const goodsNumerData = ref({});
|
||||
const selectedRows = ref([]); // 选中的行数据
|
||||
const selectAll = ref(false); // 全选状态
|
||||
const batchAuditModalVisible = ref(false); // 批量审核弹框显示状态
|
||||
const batchAuditSubmitLoading = ref(false);
|
||||
const batchAuditForm = ref({
|
||||
auth_flag: 1,
|
||||
reason: ''
|
||||
});
|
||||
const goodsGroupFlag = ref(false);
|
||||
const goodsGroupLoading = ref(false);
|
||||
const goodsGroupList = ref([]);
|
||||
const goodsGroupForm = ref({
|
||||
groupId: ""
|
||||
});
|
||||
const goodsGroupRule = reactive({
|
||||
groupId: [{ required: true, message: "请选择商品分组", trigger: "change" }]
|
||||
});
|
||||
|
||||
const goodsStatusWithCount = computed(() => {
|
||||
return [
|
||||
{title: '全部', value: 'ALL'},
|
||||
{title: `出售中${goodsNumerData.value.upperGoodsNum ? '(' + goodsNumerData.value.upperGoodsNum + ')' : ''}`, value: 'UPPER'},
|
||||
{title: `仓库中${goodsNumerData.value.downGoodsNum ? '(' + goodsNumerData.value.downGoodsNum + ')' : ''}`, value: 'DOWN'},
|
||||
{title: `待审核${goodsNumerData.value.auditGoodsNum ? '(' + goodsNumerData.value.auditGoodsNum + ')' : ''}`, value: 'TOBEAUDITED'},
|
||||
{title: `审核未通过${goodsNumerData.value.refuseGoodsNum ? '(' + goodsNumerData.value.refuseGoodsNum + ')' : ''}`, value: 'REFUSE'}
|
||||
];
|
||||
});
|
||||
|
||||
function clearTableSelection() {
|
||||
table.value?.clearSelection?.();
|
||||
}
|
||||
function onStatusTabClick(tab) {
|
||||
const status = tab.paneName ?? tab.props?.name;
|
||||
goodsStatusClick(status);
|
||||
}
|
||||
function salesModelText(v) {
|
||||
if (v === "RETAIL") return "零售";
|
||||
if (v === "WHOLESALE") return "批发";
|
||||
return "其他类型";
|
||||
}
|
||||
function goodsTypeText(v) {
|
||||
if (v === "PHYSICAL_GOODS") return "实物商品";
|
||||
if (v === "VIRTUAL_GOODS") return "虚拟商品";
|
||||
if (v === "E_COUPON") return "电子卡券"; // 卡密商品
|
||||
if (v === "APPOINTMENT_GOODS") return "预约商品";
|
||||
return v || "—";
|
||||
}
|
||||
function marketEnableText(v) {
|
||||
if (v === "DOWN") return "下架";
|
||||
if (v === "UPPER") return "上架";
|
||||
return "";
|
||||
}
|
||||
function authFlagText(v) {
|
||||
if (v === "TOBEAUDITED") return "待审核";
|
||||
if (v === "PASS") return "通过";
|
||||
if (v === "REFUSE") return "拒绝";
|
||||
return "";
|
||||
}
|
||||
// 初始化数据
|
||||
function init() {
|
||||
getDataList();
|
||||
getNumberData();
|
||||
loadGoodsGroupList();
|
||||
}
|
||||
// 分页 改变页码
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
// 分页 改变页数
|
||||
function changePageSize(v) {
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
// 搜索
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
getNumberData();
|
||||
}
|
||||
// 获取数据
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getGoodsListData(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function getNumberData() {
|
||||
// 创建一个不包含goodsStatus字段的搜索参数
|
||||
const { goodsStatus, ...searchParams } = searchForm;
|
||||
getGoodsNumerData(searchParams).then((res) => {
|
||||
if (res.success) {
|
||||
goodsNumerData.value = res.result;
|
||||
}
|
||||
})
|
||||
}
|
||||
// 编辑
|
||||
function edit(v) {
|
||||
id.value = v.id;
|
||||
if (v.underMessage) {
|
||||
underForm.reason = v.underMessage;
|
||||
} else {
|
||||
underForm.reason = "";
|
||||
}
|
||||
modalVisible.value = true;
|
||||
}
|
||||
// 下架
|
||||
function lower() {
|
||||
const reason = underForm.reason ? underForm.reason.trim() : "";
|
||||
if (!reason) {
|
||||
ElMessage.error("下架原因不能为空");
|
||||
return;
|
||||
}
|
||||
let params = {
|
||||
goodsId: id.value,
|
||||
reason
|
||||
};
|
||||
lowGoods(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
modalVisible.value = false;
|
||||
getDataList();
|
||||
getNumberData(); // 添加这行
|
||||
}
|
||||
});
|
||||
}
|
||||
// 上架
|
||||
function upper(v) {
|
||||
ElMessageBox.confirm("您确认要上架 " + v.goodsName + " ?", "确认上架", { type: "warning" }).then(() => {
|
||||
let params = {
|
||||
goodsId: v.id
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
goodsStatusWithCount() {
|
||||
return [
|
||||
{title: '全部', value: 'ALL'},
|
||||
{title: `出售中${this.goodsNumerData.upperGoodsNum ? '(' + this.goodsNumerData.upperGoodsNum + ')' : ''}`, value: 'UPPER'},
|
||||
{title: `仓库中${this.goodsNumerData.downGoodsNum ? '(' + this.goodsNumerData.downGoodsNum + ')' : ''}`, value: 'DOWN'},
|
||||
{title: `待审核${this.goodsNumerData.auditGoodsNum ? '(' + this.goodsNumerData.auditGoodsNum + ')' : ''}`, value: 'TOBEAUDITED'},
|
||||
{title: `审核未通过${this.goodsNumerData.refuseGoodsNum ? '(' + this.goodsNumerData.refuseGoodsNum + ')' : ''}`, value: 'REFUSE'}
|
||||
];
|
||||
return upGoods(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("上架成功");
|
||||
getDataList();
|
||||
getNumberData(); // 添加这行
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
//查看商品详情
|
||||
function showDetail(v) {
|
||||
customRouterPush({
|
||||
name: "goods-detail",
|
||||
query: { id: v.id },
|
||||
});
|
||||
}
|
||||
|
||||
// 商品状态筛选
|
||||
function goodsStatusClick(name) {
|
||||
if (name === "ALL" || name === "" || name === undefined || name === null) {
|
||||
delete searchForm.goodsStatus;
|
||||
currentStatus.value = "ALL";
|
||||
} else {
|
||||
searchForm.goodsStatus = name;
|
||||
currentStatus.value = name;
|
||||
}
|
||||
selectedRows.value = [];
|
||||
clearTableSelection();
|
||||
getDataList();
|
||||
}
|
||||
function examine(v, authFlag) {
|
||||
// 审核商品
|
||||
let examine = "通过";
|
||||
goodsAuditForm.authFlag = "PASS";
|
||||
if (authFlag != 1) {
|
||||
examine = "拒绝";
|
||||
goodsAuditForm.authFlag = "REFUSE";
|
||||
}
|
||||
|
||||
ElMessageBox.confirm("您确认要审核" + examine + " " + v.goodsName + " ?", "确认审核", { type: "warning" }).then(() => {
|
||||
goodsAuditForm.goodsIds=v.id;
|
||||
let formData = new FormData();
|
||||
formData.append('goodsIds', v.id);
|
||||
formData.append('authFlag', goodsAuditForm.authFlag);
|
||||
|
||||
return authGoods(formData).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("审核成功");
|
||||
getDataList();
|
||||
getNumberData();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
// 打开审核弹框
|
||||
function openAuditModal(goods) {
|
||||
currentAuditGoods.value = goods;
|
||||
goodsAuditForm.auth_flag = 1;
|
||||
goodsAuditForm.reason = '';
|
||||
auditSubmitLoading.value = false;
|
||||
auditModalVisible.value = true;
|
||||
}
|
||||
|
||||
// 提交审核
|
||||
function submitAudit() {
|
||||
if (!currentAuditGoods.value) {
|
||||
return;
|
||||
}
|
||||
auditSubmitLoading.value = true;
|
||||
const formData = new FormData();
|
||||
formData.append('goodsIds', currentAuditGoods.value.id);
|
||||
formData.append('authFlag', goodsAuditForm.auth_flag === 1 ? 'PASS' : 'REFUSE');
|
||||
authGoods(formData).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success('审核成功');
|
||||
auditModalVisible.value = false;
|
||||
getDataList();
|
||||
getNumberData();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
unitPrice,
|
||||
clearTableSelection() {
|
||||
this.$refs.table?.clearSelection?.();
|
||||
},
|
||||
onStatusTabClick(tab) {
|
||||
const status = tab.paneName ?? tab.props?.name;
|
||||
this.goodsStatusClick(status);
|
||||
},
|
||||
salesModelText(v) {
|
||||
if (v === "RETAIL") return "零售";
|
||||
if (v === "WHOLESALE") return "批发";
|
||||
return "其他类型";
|
||||
},
|
||||
goodsTypeText(v) {
|
||||
if (v === "PHYSICAL_GOODS") return "实物商品";
|
||||
if (v === "VIRTUAL_GOODS") return "虚拟商品";
|
||||
if (v === "E_COUPON") return "电子卡券"; // 卡密商品
|
||||
if (v === "APPOINTMENT_GOODS") return "预约商品";
|
||||
return v || "—";
|
||||
},
|
||||
marketEnableText(v) {
|
||||
if (v === "DOWN") return "下架";
|
||||
if (v === "UPPER") return "上架";
|
||||
return "";
|
||||
},
|
||||
authFlagText(v) {
|
||||
if (v === "TOBEAUDITED") return "待审核";
|
||||
if (v === "PASS") return "通过";
|
||||
if (v === "REFUSE") return "拒绝";
|
||||
return "";
|
||||
},
|
||||
// 初始化数据
|
||||
init() {
|
||||
this.getDataList();
|
||||
this.getNumberData();
|
||||
this.loadGoodsGroupList();
|
||||
},
|
||||
// 分页 改变页码
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
// 分页 改变页数
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
// 搜索
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
this.getNumberData();
|
||||
},
|
||||
// 获取数据
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getGoodsListData(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
getNumberData() {
|
||||
// 创建一个不包含goodsStatus字段的搜索参数
|
||||
const { goodsStatus, ...searchParams } = this.searchForm;
|
||||
getGoodsNumerData(searchParams).then((res) => {
|
||||
if (res.success) {
|
||||
this.goodsNumerData = res.result;
|
||||
}
|
||||
})
|
||||
},
|
||||
// 编辑
|
||||
edit(v) {
|
||||
this.id = v.id;
|
||||
if (v.underMessage) {
|
||||
this.underForm.reason = v.underMessage;
|
||||
} else {
|
||||
this.underForm.reason = "";
|
||||
}).finally(() => {
|
||||
auditSubmitLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function onSelectionChange(selection) {
|
||||
selectedRows.value = selection;
|
||||
}
|
||||
|
||||
// 批量上架
|
||||
function batchUpper() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning('请先选择要上架的商品');
|
||||
return;
|
||||
}
|
||||
|
||||
const goodsNames = selectedRows.value.map(item => item.goodsName).join('、');
|
||||
ElMessageBox.confirm(`您确认要上架以下商品吗?\n${goodsNames}`, "确认批量上架", { type: "warning" }).then(() => {
|
||||
const goodsIds = selectedRows.value.map(item => item.id);
|
||||
const params = {
|
||||
goodsId: goodsIds
|
||||
};
|
||||
|
||||
return upGoods(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success('批量上架成功');
|
||||
selectedRows.value = [];
|
||||
selectAll.value = false;
|
||||
getDataList();
|
||||
getNumberData();
|
||||
}
|
||||
this.modalVisible = true;
|
||||
},
|
||||
// 下架
|
||||
lower() {
|
||||
const reason = this.underForm.reason ? this.underForm.reason.trim() : "";
|
||||
if (!reason) {
|
||||
ElMessage.error("下架原因不能为空");
|
||||
return;
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
// 批量下架
|
||||
function batchLower() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning('请先选择要下架的商品');
|
||||
return;
|
||||
}
|
||||
|
||||
const goodsNames = selectedRows.value.map(item => item.goodsName).join('、');
|
||||
ElMessageBox.confirm(`您确认要下架以下商品吗?\n${goodsNames}`, "确认批量下架", { type: "warning" }).then(() => {
|
||||
const goodsIds = selectedRows.value.map(item => item.id);
|
||||
const params = {
|
||||
goodsId: goodsIds,
|
||||
reason: '批量下架操作'
|
||||
};
|
||||
|
||||
return lowGoods(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success('批量下架成功');
|
||||
selectedRows.value = [];
|
||||
selectAll.value = false;
|
||||
getDataList();
|
||||
getNumberData();
|
||||
}
|
||||
let params = {
|
||||
goodsId: this.id,
|
||||
reason
|
||||
};
|
||||
lowGoods(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.modalVisible = false;
|
||||
this.getDataList();
|
||||
this.getNumberData(); // 添加这行
|
||||
}
|
||||
});
|
||||
},
|
||||
// 上架
|
||||
upper(v) {
|
||||
ElMessageBox.confirm("您确认要上架 " + v.goodsName + " ?", "确认上架", { type: "warning" }).then(() => {
|
||||
let params = {
|
||||
goodsId: v.id
|
||||
};
|
||||
return upGoods(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("上架成功");
|
||||
this.getDataList();
|
||||
this.getNumberData(); // 添加这行
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
//查看商品详情
|
||||
showDetail(v) {
|
||||
customRouterPush({
|
||||
name: "goods-detail",
|
||||
query: { id: v.id },
|
||||
});
|
||||
},
|
||||
// 批量审核
|
||||
function batchAudit() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning('请先选择要审核的商品');
|
||||
return;
|
||||
}
|
||||
|
||||
// 商品状态筛选
|
||||
goodsStatusClick(name) {
|
||||
if (name === "ALL" || name === "" || name === undefined || name === null) {
|
||||
delete this.searchForm.goodsStatus;
|
||||
this.currentStatus = "ALL";
|
||||
} else {
|
||||
this.searchForm.goodsStatus = name;
|
||||
this.currentStatus = name;
|
||||
}
|
||||
this.selectedRows = [];
|
||||
this.clearTableSelection();
|
||||
this.getDataList();
|
||||
},
|
||||
examine(v, authFlag) {
|
||||
// 审核商品
|
||||
let examine = "通过";
|
||||
this.goodsAuditForm.authFlag = "PASS";
|
||||
if (authFlag != 1) {
|
||||
examine = "拒绝";
|
||||
this.goodsAuditForm.authFlag = "REFUSE";
|
||||
}
|
||||
// 重置批量审核表单
|
||||
batchAuditForm.value = {
|
||||
auth_flag: 1,
|
||||
reason: ''
|
||||
};
|
||||
batchAuditModalVisible.value = true;
|
||||
}
|
||||
|
||||
ElMessageBox.confirm("您确认要审核" + examine + " " + v.goodsName + " ?", "确认审核", { type: "warning" }).then(() => {
|
||||
this.goodsAuditForm.goodsIds=v.id;
|
||||
let formData = new FormData();
|
||||
formData.append('goodsIds', v.id);
|
||||
formData.append('authFlag', this.goodsAuditForm.authFlag);
|
||||
// 提交批量审核
|
||||
function submitBatchAudit() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning('请先选择要审核的商品');
|
||||
return;
|
||||
}
|
||||
|
||||
return authGoods(formData).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("审核成功");
|
||||
this.getDataList();
|
||||
this.getNumberData();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
// 如果是拒绝审核,必须填写原因
|
||||
if (batchAuditForm.value.auth_flag === 2 && !batchAuditForm.value.reason.trim()) {
|
||||
ElMessage.warning('审核拒绝时必须填写拒绝原因');
|
||||
return;
|
||||
}
|
||||
|
||||
// 打开审核弹框
|
||||
openAuditModal(goods) {
|
||||
this.currentAuditGoods = goods;
|
||||
this.goodsAuditForm.auth_flag = 1;
|
||||
this.goodsAuditForm.reason = '';
|
||||
this.auditSubmitLoading = false;
|
||||
this.auditModalVisible = true;
|
||||
},
|
||||
const actionText = batchAuditForm.value.auth_flag === 1 ? '通过' : '拒绝';
|
||||
const goodsIds = selectedRows.value.map(item => item.id);
|
||||
const formData = new FormData();
|
||||
formData.append('goodsIds', goodsIds.join(','));
|
||||
formData.append('authFlag', batchAuditForm.value.auth_flag === 1 ? 'PASS' : 'REFUSE');
|
||||
if (batchAuditForm.value.reason) {
|
||||
formData.append('reason', batchAuditForm.value.reason);
|
||||
}
|
||||
|
||||
// 提交审核
|
||||
submitAudit() {
|
||||
if (!this.currentAuditGoods) {
|
||||
return;
|
||||
}
|
||||
this.auditSubmitLoading = true;
|
||||
const formData = new FormData();
|
||||
formData.append('goodsIds', this.currentAuditGoods.id);
|
||||
formData.append('authFlag', this.goodsAuditForm.auth_flag === 1 ? 'PASS' : 'REFUSE');
|
||||
authGoods(formData).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success('审核成功');
|
||||
this.auditModalVisible = false;
|
||||
this.getDataList();
|
||||
this.getNumberData();
|
||||
}
|
||||
}).finally(() => {
|
||||
this.auditSubmitLoading = false;
|
||||
});
|
||||
},
|
||||
|
||||
onSelectionChange(selection) {
|
||||
this.selectedRows = selection;
|
||||
},
|
||||
|
||||
// 批量上架
|
||||
batchUpper() {
|
||||
if (this.selectedRows.length === 0) {
|
||||
ElMessage.warning('请先选择要上架的商品');
|
||||
return;
|
||||
}
|
||||
|
||||
const goodsNames = this.selectedRows.map(item => item.goodsName).join('、');
|
||||
ElMessageBox.confirm(`您确认要上架以下商品吗?\n${goodsNames}`, "确认批量上架", { type: "warning" }).then(() => {
|
||||
const goodsIds = this.selectedRows.map(item => item.id);
|
||||
const params = {
|
||||
goodsId: goodsIds
|
||||
};
|
||||
|
||||
return upGoods(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success('批量上架成功');
|
||||
this.selectedRows = [];
|
||||
this.selectAll = false;
|
||||
this.getDataList();
|
||||
this.getNumberData();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
// 批量下架
|
||||
batchLower() {
|
||||
if (this.selectedRows.length === 0) {
|
||||
ElMessage.warning('请先选择要下架的商品');
|
||||
return;
|
||||
}
|
||||
|
||||
const goodsNames = this.selectedRows.map(item => item.goodsName).join('、');
|
||||
ElMessageBox.confirm(`您确认要下架以下商品吗?\n${goodsNames}`, "确认批量下架", { type: "warning" }).then(() => {
|
||||
const goodsIds = this.selectedRows.map(item => item.id);
|
||||
const params = {
|
||||
goodsId: goodsIds,
|
||||
reason: '批量下架操作'
|
||||
};
|
||||
|
||||
return lowGoods(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success('批量下架成功');
|
||||
this.selectedRows = [];
|
||||
this.selectAll = false;
|
||||
this.getDataList();
|
||||
this.getNumberData();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
|
||||
// 批量审核
|
||||
batchAudit() {
|
||||
if (this.selectedRows.length === 0) {
|
||||
ElMessage.warning('请先选择要审核的商品');
|
||||
return;
|
||||
}
|
||||
|
||||
// 重置批量审核表单
|
||||
this.batchAuditForm = {
|
||||
auth_flag: 1,
|
||||
reason: ''
|
||||
};
|
||||
this.batchAuditModalVisible = true;
|
||||
},
|
||||
|
||||
// 提交批量审核
|
||||
submitBatchAudit() {
|
||||
if (this.selectedRows.length === 0) {
|
||||
ElMessage.warning('请先选择要审核的商品');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是拒绝审核,必须填写原因
|
||||
if (this.batchAuditForm.auth_flag === 2 && !this.batchAuditForm.reason.trim()) {
|
||||
ElMessage.warning('审核拒绝时必须填写拒绝原因');
|
||||
return;
|
||||
}
|
||||
|
||||
const actionText = this.batchAuditForm.auth_flag === 1 ? '通过' : '拒绝';
|
||||
const goodsIds = this.selectedRows.map(item => item.id);
|
||||
const formData = new FormData();
|
||||
formData.append('goodsIds', goodsIds.join(','));
|
||||
formData.append('authFlag', this.batchAuditForm.auth_flag === 1 ? 'PASS' : 'REFUSE');
|
||||
if (this.batchAuditForm.reason) {
|
||||
formData.append('reason', this.batchAuditForm.reason);
|
||||
}
|
||||
|
||||
this.batchAuditSubmitLoading = true;
|
||||
authGoods(formData).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success(`批量审核${actionText}成功`);
|
||||
this.selectedRows = [];
|
||||
this.selectAll = false;
|
||||
this.batchAuditModalVisible = false;
|
||||
this.clearTableSelection();
|
||||
this.getDataList();
|
||||
this.getNumberData();
|
||||
}
|
||||
}).finally(() => {
|
||||
this.batchAuditSubmitLoading = false;
|
||||
});
|
||||
},
|
||||
loadGoodsGroupList() {
|
||||
getGoodsGroupByPage({ pageNumber: 1, pageSize: 1000 }).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
this.goodsGroupList = res.result.records || [];
|
||||
}
|
||||
});
|
||||
},
|
||||
openSetGoodsGroup() {
|
||||
if (this.selectedRows.length === 0) {
|
||||
ElMessage.warning("请先选择商品");
|
||||
return;
|
||||
}
|
||||
this.goodsGroupFlag = true;
|
||||
this.goodsGroupLoading = false;
|
||||
this.goodsGroupForm = { groupId: "" };
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.goodsGroupForm) this.$refs.goodsGroupForm.resetFields();
|
||||
});
|
||||
this.loadGoodsGroupList();
|
||||
},
|
||||
submitSetGoodsGroup() {
|
||||
this.$refs.goodsGroupForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
const goodsIds = this.selectedRows.map((item) => item.id);
|
||||
this.goodsGroupLoading = true;
|
||||
addGoodsGroupItems(this.goodsGroupForm.groupId, goodsIds).then((res) => {
|
||||
this.goodsGroupLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("设置成功");
|
||||
this.goodsGroupFlag = false;
|
||||
this.selectedRows = [];
|
||||
this.selectAll = false;
|
||||
this.clearTableSelection();
|
||||
this.getDataList();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
}).catch(() => {
|
||||
this.goodsGroupLoading = false;
|
||||
ElMessage.error("设置失败,请检查权限或后端接口");
|
||||
});
|
||||
});
|
||||
batchAuditSubmitLoading.value = true;
|
||||
authGoods(formData).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success(`批量审核${actionText}成功`);
|
||||
selectedRows.value = [];
|
||||
selectAll.value = false;
|
||||
batchAuditModalVisible.value = false;
|
||||
clearTableSelection();
|
||||
getDataList();
|
||||
getNumberData();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
}).finally(() => {
|
||||
batchAuditSubmitLoading.value = false;
|
||||
});
|
||||
}
|
||||
function loadGoodsGroupList() {
|
||||
getGoodsGroupByPage({ pageNumber: 1, pageSize: 1000 }).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
goodsGroupList.value = res.result.records || [];
|
||||
}
|
||||
});
|
||||
}
|
||||
function openSetGoodsGroup() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning("请先选择商品");
|
||||
return;
|
||||
}
|
||||
goodsGroupFlag.value = true;
|
||||
goodsGroupLoading.value = false;
|
||||
goodsGroupForm.value = { groupId: "" };
|
||||
nextTick(() => {
|
||||
if (goodsGroupFormRef.value) goodsGroupFormRef.value.resetFields();
|
||||
});
|
||||
loadGoodsGroupList();
|
||||
}
|
||||
function submitSetGoodsGroup() {
|
||||
goodsGroupFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
const goodsIds = selectedRows.value.map((item) => item.id);
|
||||
goodsGroupLoading.value = true;
|
||||
addGoodsGroupItems(goodsGroupForm.value.groupId, goodsIds).then((res) => {
|
||||
goodsGroupLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("设置成功");
|
||||
goodsGroupFlag.value = false;
|
||||
selectedRows.value = [];
|
||||
selectAll.value = false;
|
||||
clearTableSelection();
|
||||
getDataList();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
}).catch(() => {
|
||||
goodsGroupLoading.value = false;
|
||||
ElMessage.error("设置失败,请检查权限或后端接口");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
// Tab组件样式
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -87,104 +87,103 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { authGoods, getAuthGoodsListData } from "@/api/goods";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { unitPrice, customRouterPush } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "goodsApply",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
},
|
||||
goodsAuditForm: {
|
||||
auth_flag: 1,
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
unitPrice,
|
||||
authFlagText(v) {
|
||||
const map = {
|
||||
TOBEAUDITED: "待审核",
|
||||
PASS: "审核通过",
|
||||
REFUSE: "审核拒绝",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
},
|
||||
authFlagTagType(v) {
|
||||
const map = {
|
||||
TOBEAUDITED: "warning",
|
||||
PASS: "success",
|
||||
REFUSE: "danger",
|
||||
};
|
||||
return map[v] || "info";
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
this.searchForm.authFlag = 0;
|
||||
getAuthGoodsListData(this.searchForm)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
examine(v, authFlag) {
|
||||
const examine = authFlag === 1 ? "通过" : "拒绝";
|
||||
this.goodsAuditForm.authFlag = authFlag === 1 ? "PASS" : "REFUSE";
|
||||
ElMessageBox.confirm(`您确认要审核${examine} ${v.goodsName} ?`, "确认审核", { type: "warning" }).then(() => {
|
||||
const formData = new FormData();
|
||||
formData.append("goodsIds", v.id);
|
||||
formData.append("authFlag", this.goodsAuditForm.authFlag);
|
||||
return authGoods(formData).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("审核成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
showDetail(v) {
|
||||
customRouterPush({
|
||||
name: "goods-detail",
|
||||
query: { id: v.id },
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "goodsApply" });
|
||||
|
||||
// 模板中 el-form 的 ref 与数据 searchForm 同名,避免相互覆盖改名
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
});
|
||||
const goodsAuditForm = ref({
|
||||
auth_flag: 1,
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
function authFlagText(v) {
|
||||
const map = {
|
||||
TOBEAUDITED: "待审核",
|
||||
PASS: "审核通过",
|
||||
REFUSE: "审核拒绝",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
}
|
||||
function authFlagTagType(v) {
|
||||
const map = {
|
||||
TOBEAUDITED: "warning",
|
||||
PASS: "success",
|
||||
REFUSE: "danger",
|
||||
};
|
||||
return map[v] || "info";
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.value.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.value.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.value.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
searchForm.value.authFlag = 0;
|
||||
getAuthGoodsListData(searchForm.value)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function examine(v, authFlag) {
|
||||
const examine = authFlag === 1 ? "通过" : "拒绝";
|
||||
goodsAuditForm.value.authFlag = authFlag === 1 ? "PASS" : "REFUSE";
|
||||
ElMessageBox.confirm(`您确认要审核${examine} ${v.goodsName} ?`, "确认审核", { type: "warning" }).then(() => {
|
||||
const formData = new FormData();
|
||||
formData.append("goodsIds", v.id);
|
||||
formData.append("authFlag", goodsAuditForm.value.authFlag);
|
||||
return authGoods(formData).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("审核成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
function showDetail(v) {
|
||||
customRouterPush({
|
||||
name: "goods-detail",
|
||||
query: { id: v.id },
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -376,307 +376,304 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, reactive, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { View } from "@element-plus/icons-vue";
|
||||
import { authGoods, getBrandListData, getGoodsDetail } from "@/api/goods";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "goodsDetail",
|
||||
components: { View },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
goods: {},
|
||||
brandList: [],
|
||||
previewGoodsPicture: "",
|
||||
goodsPictureVisible: false,
|
||||
wholesaleData: [],
|
||||
skuData: [],
|
||||
activeTab: "basic",
|
||||
slotCapacityVisible: false,
|
||||
currentSlotCapacities: [],
|
||||
goodsAuditForm: {
|
||||
auth_flag: 1,
|
||||
},
|
||||
auditModalVisible: false,
|
||||
auditSubmitLoading: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isAppointmentGoods() {
|
||||
return this.goods.goodsType === "APPOINTMENT_GOODS";
|
||||
},
|
||||
needsLogistics() {
|
||||
return this.goods.goodsType === "PHYSICAL_GOODS";
|
||||
},
|
||||
salesModelDisplayText() {
|
||||
const type = this.goods.goodsType;
|
||||
const model = this.goods.salesModel;
|
||||
if (type === "APPOINTMENT_GOODS") return "预约型";
|
||||
if (type === "E_COUPON") return "电子卡券";
|
||||
if (type === "VIRTUAL_GOODS") return "虚拟型";
|
||||
if (model === "WHOLESALE") return "批发型";
|
||||
if (model === "RETAIL") return "零售型";
|
||||
return model || "—";
|
||||
},
|
||||
sectionTabs() {
|
||||
const tabs = [
|
||||
{ name: "basic", label: "基础信息" },
|
||||
{ name: "media", label: "图文信息" },
|
||||
];
|
||||
if (this.isAppointmentGoods) {
|
||||
tabs.push({ name: "appointment", label: "预约规则" });
|
||||
}
|
||||
tabs.push(
|
||||
{ name: "price", label: "价格库存" },
|
||||
{ name: "other", label: "其他信息" },
|
||||
);
|
||||
return tabs;
|
||||
},
|
||||
categoryText() {
|
||||
const names = this.goods.categoryName;
|
||||
if (!names || !names.length) return "—";
|
||||
return names.join(" > ");
|
||||
},
|
||||
/** 兼容扁平列表与历史嵌套分组两种 params 结构 */
|
||||
displayGoodsParams() {
|
||||
let list = this.goods.goodsParamsDTOList;
|
||||
if ((!Array.isArray(list) || !list.length) && this.goods.params) {
|
||||
list = this.parseParamsJson(this.goods.params);
|
||||
}
|
||||
if (!Array.isArray(list) || !list.length) return [];
|
||||
const flat = [];
|
||||
list.forEach((item) => {
|
||||
if (Array.isArray(item?.goodsParamsItemDTOList) && item.goodsParamsItemDTOList.length) {
|
||||
item.goodsParamsItemDTOList.forEach((child) => {
|
||||
if (child?.paramName) flat.push(child);
|
||||
});
|
||||
} else if (item?.paramName) {
|
||||
flat.push(item);
|
||||
}
|
||||
defineOptions({ name: "goodsDetail" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const loading = ref(false);
|
||||
const goods = ref({});
|
||||
const brandList = ref([]);
|
||||
const previewGoodsPicture = ref("");
|
||||
const goodsPictureVisible = ref(false);
|
||||
const wholesaleData = ref([]);
|
||||
const skuData = ref([]);
|
||||
const activeTab = ref("basic");
|
||||
const slotCapacityVisible = ref(false);
|
||||
const currentSlotCapacities = ref([]);
|
||||
const goodsAuditForm = reactive({
|
||||
auth_flag: 1,
|
||||
});
|
||||
const auditModalVisible = ref(false);
|
||||
const auditSubmitLoading = ref(false);
|
||||
|
||||
const isAppointmentGoods = computed(() => {
|
||||
return goods.value.goodsType === "APPOINTMENT_GOODS";
|
||||
});
|
||||
const needsLogistics = computed(() => {
|
||||
return goods.value.goodsType === "PHYSICAL_GOODS";
|
||||
});
|
||||
const salesModelDisplayText = computed(() => {
|
||||
const type = goods.value.goodsType;
|
||||
const model = goods.value.salesModel;
|
||||
if (type === "APPOINTMENT_GOODS") return "预约型";
|
||||
if (type === "E_COUPON") return "电子卡券";
|
||||
if (type === "VIRTUAL_GOODS") return "虚拟型";
|
||||
if (model === "WHOLESALE") return "批发型";
|
||||
if (model === "RETAIL") return "零售型";
|
||||
return model || "—";
|
||||
});
|
||||
const sectionTabs = computed(() => {
|
||||
const tabs = [
|
||||
{ name: "basic", label: "基础信息" },
|
||||
{ name: "media", label: "图文信息" },
|
||||
];
|
||||
if (isAppointmentGoods.value) {
|
||||
tabs.push({ name: "appointment", label: "预约规则" });
|
||||
}
|
||||
tabs.push(
|
||||
{ name: "price", label: "价格库存" },
|
||||
{ name: "other", label: "其他信息" },
|
||||
);
|
||||
return tabs;
|
||||
});
|
||||
const categoryText = computed(() => {
|
||||
const names = goods.value.categoryName;
|
||||
if (!names || !names.length) return "—";
|
||||
return names.join(" > ");
|
||||
});
|
||||
/** 兼容扁平列表与历史嵌套分组两种 params 结构 */
|
||||
const displayGoodsParams = computed(() => {
|
||||
let list = goods.value.goodsParamsDTOList;
|
||||
if ((!Array.isArray(list) || !list.length) && goods.value.params) {
|
||||
list = parseParamsJson(goods.value.params);
|
||||
}
|
||||
if (!Array.isArray(list) || !list.length) return [];
|
||||
const flat = [];
|
||||
list.forEach((item) => {
|
||||
if (Array.isArray(item?.goodsParamsItemDTOList) && item.goodsParamsItemDTOList.length) {
|
||||
item.goodsParamsItemDTOList.forEach((child) => {
|
||||
if (child?.paramName) flat.push(child);
|
||||
});
|
||||
return flat;
|
||||
},
|
||||
appointmentSettings() {
|
||||
return this.goods.appointmentSettings || null;
|
||||
},
|
||||
appointmentSlotTemplate() {
|
||||
return this.goods.appointmentSlotTemplate || null;
|
||||
},
|
||||
appointmentSlots() {
|
||||
return this.appointmentSlotTemplate?.slots || [];
|
||||
},
|
||||
bookableDateText() {
|
||||
const s = this.appointmentSettings;
|
||||
if (!s) return "—";
|
||||
if (s.bookableDateMode === "CUSTOM") {
|
||||
const parts = [];
|
||||
if (s.bookableCustomStartDate && s.bookableCustomEndDate) {
|
||||
parts.push(`${s.bookableCustomStartDate} ~ ${s.bookableCustomEndDate}`);
|
||||
}
|
||||
const weekdays = this.formatWeekdays(s.bookableWeekdays);
|
||||
if (weekdays) parts.push(weekdays);
|
||||
return parts.length ? parts.join(";") : "自定义";
|
||||
} else if (item?.paramName) {
|
||||
flat.push(item);
|
||||
}
|
||||
});
|
||||
return flat;
|
||||
});
|
||||
const appointmentSettings = computed(() => {
|
||||
return goods.value.appointmentSettings || null;
|
||||
});
|
||||
const appointmentSlotTemplate = computed(() => {
|
||||
return goods.value.appointmentSlotTemplate || null;
|
||||
});
|
||||
const appointmentSlots = computed(() => {
|
||||
return appointmentSlotTemplate.value?.slots || [];
|
||||
});
|
||||
const bookableDateText = computed(() => {
|
||||
const s = appointmentSettings.value;
|
||||
if (!s) return "—";
|
||||
if (s.bookableDateMode === "CUSTOM") {
|
||||
const parts = [];
|
||||
if (s.bookableCustomStartDate && s.bookableCustomEndDate) {
|
||||
parts.push(`${s.bookableCustomStartDate} ~ ${s.bookableCustomEndDate}`);
|
||||
}
|
||||
const weekdays = formatWeekdays(s.bookableWeekdays);
|
||||
if (weekdays) parts.push(weekdays);
|
||||
return parts.length ? parts.join(";") : "自定义";
|
||||
}
|
||||
if (s.bookableDateMode === "EVERY_DAY") return "每天";
|
||||
return "—";
|
||||
});
|
||||
const bookableDaysText = computed(() => {
|
||||
const s = appointmentSettings.value;
|
||||
if (!s || s.bookableDays == null) return "—";
|
||||
return `对用户展示 ${s.bookableDays} 天内的可预约日期`;
|
||||
});
|
||||
const advanceBookingText = computed(() => {
|
||||
const s = appointmentSettings.value;
|
||||
if (!s) return "—";
|
||||
if (s.advanceMode === "NONE") return "无需提前预约";
|
||||
if (s.advanceMode === "REQUIRE_HOURS" && s.advanceHours != null) {
|
||||
return `需提前 ${s.advanceHours} 小时进行预约`;
|
||||
}
|
||||
return "—";
|
||||
});
|
||||
const cancelRuleText = computed(() => {
|
||||
const s = appointmentSettings.value;
|
||||
if (!s) return "—";
|
||||
if (s.cancelMode === "NOT_ALLOWED") return "不允许取消";
|
||||
if (s.cancelMode === "ALLOW_BEFORE_HOURS" && s.cancelAheadHours != null) {
|
||||
return `服务开始 ${s.cancelAheadHours} 小时之前,允许取消并自动退款`;
|
||||
}
|
||||
return "—";
|
||||
});
|
||||
const formSubmitText = computed(() => {
|
||||
const s = appointmentSettings.value;
|
||||
if (!s) return "—";
|
||||
if (s.formSubmitMode === "PER_APPOINTMENT") return "每个预约提交一次";
|
||||
if (s.formSubmitMode === "PER_ORDER") return "每单提交一次";
|
||||
return "—";
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadBrandList();
|
||||
initGoods(route.query.id);
|
||||
});
|
||||
|
||||
function sectionNo(name) {
|
||||
const index = sectionTabs.value.findIndex((tab) => tab.name === name);
|
||||
return index >= 0 ? index + 1 : "";
|
||||
}
|
||||
function parseParamsJson(paramsJson) {
|
||||
if (!paramsJson) return [];
|
||||
try {
|
||||
const array = JSON.parse(paramsJson);
|
||||
if (!Array.isArray(array)) return [];
|
||||
const items = [];
|
||||
array.forEach((element) => {
|
||||
if (Array.isArray(element?.goodsParamsItemDTOList)) {
|
||||
items.push(...element.goodsParamsItemDTOList);
|
||||
} else if (element?.paramName) {
|
||||
items.push(element);
|
||||
}
|
||||
if (s.bookableDateMode === "EVERY_DAY") return "每天";
|
||||
return "—";
|
||||
},
|
||||
bookableDaysText() {
|
||||
const s = this.appointmentSettings;
|
||||
if (!s || s.bookableDays == null) return "—";
|
||||
return `对用户展示 ${s.bookableDays} 天内的可预约日期`;
|
||||
},
|
||||
advanceBookingText() {
|
||||
const s = this.appointmentSettings;
|
||||
if (!s) return "—";
|
||||
if (s.advanceMode === "NONE") return "无需提前预约";
|
||||
if (s.advanceMode === "REQUIRE_HOURS" && s.advanceHours != null) {
|
||||
return `需提前 ${s.advanceHours} 小时进行预约`;
|
||||
}
|
||||
return "—";
|
||||
},
|
||||
cancelRuleText() {
|
||||
const s = this.appointmentSettings;
|
||||
if (!s) return "—";
|
||||
if (s.cancelMode === "NOT_ALLOWED") return "不允许取消";
|
||||
if (s.cancelMode === "ALLOW_BEFORE_HOURS" && s.cancelAheadHours != null) {
|
||||
return `服务开始 ${s.cancelAheadHours} 小时之前,允许取消并自动退款`;
|
||||
}
|
||||
return "—";
|
||||
},
|
||||
formSubmitText() {
|
||||
const s = this.appointmentSettings;
|
||||
if (!s) return "—";
|
||||
if (s.formSubmitMode === "PER_APPOINTMENT") return "每个预约提交一次";
|
||||
if (s.formSubmitMode === "PER_ORDER") return "每单提交一次";
|
||||
return "—";
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadBrandList();
|
||||
this.initGoods(this.$route.query.id);
|
||||
},
|
||||
methods: {
|
||||
sectionNo(name) {
|
||||
const index = this.sectionTabs.findIndex((tab) => tab.name === name);
|
||||
return index >= 0 ? index + 1 : "";
|
||||
},
|
||||
parseParamsJson(paramsJson) {
|
||||
if (!paramsJson) return [];
|
||||
try {
|
||||
const array = JSON.parse(paramsJson);
|
||||
if (!Array.isArray(array)) return [];
|
||||
const items = [];
|
||||
array.forEach((element) => {
|
||||
if (Array.isArray(element?.goodsParamsItemDTOList)) {
|
||||
items.push(...element.goodsParamsItemDTOList);
|
||||
} else if (element?.paramName) {
|
||||
items.push(element);
|
||||
}
|
||||
});
|
||||
return items;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
jumpToTab(name) {
|
||||
this.activeTab = name;
|
||||
this.$nextTick(() => {
|
||||
const el = document.getElementById(`goods-section-${name}`);
|
||||
el?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
});
|
||||
},
|
||||
loadBrandList() {
|
||||
getBrandListData().then((res) => {
|
||||
this.brandList = Array.isArray(res) ? res : res?.result || [];
|
||||
});
|
||||
},
|
||||
initGoods(id) {
|
||||
if (!id) return;
|
||||
this.loading = true;
|
||||
getGoodsDetail(id)
|
||||
.then((res) => {
|
||||
if (!res?.success) return;
|
||||
this.goods = res.result || {};
|
||||
this.skuData = (res.result?.skuList || []).map((sku) => ({
|
||||
id: sku.id,
|
||||
specs: sku.simpleSpecs || sku.goodsName,
|
||||
sn: sku.sn,
|
||||
weight: sku.weight,
|
||||
price: sku.price,
|
||||
image: sku.goodsGalleryList,
|
||||
quantity: sku.quantity,
|
||||
slotCapacities: sku.slotCapacities,
|
||||
}));
|
||||
this.wholesaleData = res.result?.wholesaleList || [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
brandText(brandId) {
|
||||
if (!brandId || brandId === "0") return "—";
|
||||
const brand = this.brandList.find((item) => String(item.id) === String(brandId));
|
||||
return brand?.name || brandId;
|
||||
},
|
||||
goodsTypeText(v) {
|
||||
if (v === "PHYSICAL_GOODS") return "实物商品";
|
||||
if (v === "VIRTUAL_GOODS") return "虚拟商品";
|
||||
if (v === "E_COUPON") return "电子卡券";
|
||||
if (v === "APPOINTMENT_GOODS") return "预约商品";
|
||||
return v || "—";
|
||||
},
|
||||
normalizeSlotCapacities(caps) {
|
||||
if (!Array.isArray(caps) || !caps.length) return [];
|
||||
return caps.map((item) => ({
|
||||
startTime: item.startTime || "",
|
||||
endTime: item.endTime || "",
|
||||
capacity: item.slotCapacity ?? item.capacity ?? 0,
|
||||
});
|
||||
return items;
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
function jumpToTab(name) {
|
||||
activeTab.value = name;
|
||||
nextTick(() => {
|
||||
const el = document.getElementById(`goods-section-${name}`);
|
||||
el?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
});
|
||||
}
|
||||
function loadBrandList() {
|
||||
getBrandListData().then((res) => {
|
||||
brandList.value = Array.isArray(res) ? res : res?.result || [];
|
||||
});
|
||||
}
|
||||
function initGoods(id) {
|
||||
if (!id) return;
|
||||
loading.value = true;
|
||||
getGoodsDetail(id)
|
||||
.then((res) => {
|
||||
if (!res?.success) return;
|
||||
goods.value = res.result || {};
|
||||
skuData.value = (res.result?.skuList || []).map((sku) => ({
|
||||
id: sku.id,
|
||||
specs: sku.simpleSpecs || sku.goodsName,
|
||||
sn: sku.sn,
|
||||
weight: sku.weight,
|
||||
price: sku.price,
|
||||
image: sku.goodsGalleryList,
|
||||
quantity: sku.quantity,
|
||||
slotCapacities: sku.slotCapacities,
|
||||
}));
|
||||
},
|
||||
hasSlotCapacities(row) {
|
||||
return this.slotCapacityCount(row) > 0;
|
||||
},
|
||||
slotCapacityCount(row) {
|
||||
return this.normalizeSlotCapacities(row?.slotCapacities).length;
|
||||
},
|
||||
openSlotCapacityDialog(row) {
|
||||
this.currentSlotCapacities = this.normalizeSlotCapacities(row?.slotCapacities);
|
||||
this.slotCapacityVisible = true;
|
||||
},
|
||||
formatSlotCapacity(capacity) {
|
||||
const value = Number(capacity);
|
||||
if (!value) return "不限制";
|
||||
return String(value);
|
||||
},
|
||||
marketEnableText(v) {
|
||||
if (v === "UPPER") return "上架";
|
||||
if (v === "DOWN") return "下架";
|
||||
return "—";
|
||||
},
|
||||
authFlagText(v) {
|
||||
if (v === "TOBEAUDITED") return "待审核";
|
||||
if (v === "PASS") return "通过";
|
||||
if (v === "REFUSE") return "拒绝";
|
||||
return v || "—";
|
||||
},
|
||||
authFlagTagType(v) {
|
||||
if (v === "PASS") return "success";
|
||||
if (v === "TOBEAUDITED") return "warning";
|
||||
if (v === "REFUSE") return "danger";
|
||||
return "info";
|
||||
},
|
||||
formatServiceModes(modes) {
|
||||
if (!modes || !modes.length) return "—";
|
||||
const map = {
|
||||
IN_STORE: "到店服务",
|
||||
ON_SITE: "上门服务",
|
||||
};
|
||||
return modes.map((m) => map[m] || m).join("、");
|
||||
},
|
||||
formatWeekdays(weekdays) {
|
||||
if (!Array.isArray(weekdays) || !weekdays.length) return "";
|
||||
const map = {
|
||||
1: "周一",
|
||||
2: "周二",
|
||||
3: "周三",
|
||||
4: "周四",
|
||||
5: "周五",
|
||||
6: "周六",
|
||||
7: "周日",
|
||||
};
|
||||
return weekdays.map((d) => map[d] || d).join("、");
|
||||
},
|
||||
handleViewGoodsPicture(url) {
|
||||
this.previewGoodsPicture = url;
|
||||
this.goodsPictureVisible = true;
|
||||
},
|
||||
openAuditModal() {
|
||||
this.goodsAuditForm.auth_flag = 1;
|
||||
this.auditSubmitLoading = false;
|
||||
this.auditModalVisible = true;
|
||||
},
|
||||
submitAudit() {
|
||||
if (!this.goods.id) return;
|
||||
this.auditSubmitLoading = true;
|
||||
const formData = new FormData();
|
||||
formData.append("goodsIds", this.goods.id);
|
||||
formData.append("authFlag", this.goodsAuditForm.auth_flag === 1 ? "PASS" : "REFUSE");
|
||||
authGoods(formData)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("审核成功");
|
||||
this.auditModalVisible = false;
|
||||
this.initGoods(this.goods.id);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.auditSubmitLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
wholesaleData.value = res.result?.wholesaleList || [];
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function brandText(brandId) {
|
||||
if (!brandId || brandId === "0") return "—";
|
||||
const brand = brandList.value.find((item) => String(item.id) === String(brandId));
|
||||
return brand?.name || brandId;
|
||||
}
|
||||
function goodsTypeText(v) {
|
||||
if (v === "PHYSICAL_GOODS") return "实物商品";
|
||||
if (v === "VIRTUAL_GOODS") return "虚拟商品";
|
||||
if (v === "E_COUPON") return "电子卡券";
|
||||
if (v === "APPOINTMENT_GOODS") return "预约商品";
|
||||
return v || "—";
|
||||
}
|
||||
function normalizeSlotCapacities(caps) {
|
||||
if (!Array.isArray(caps) || !caps.length) return [];
|
||||
return caps.map((item) => ({
|
||||
startTime: item.startTime || "",
|
||||
endTime: item.endTime || "",
|
||||
capacity: item.slotCapacity ?? item.capacity ?? 0,
|
||||
}));
|
||||
}
|
||||
function hasSlotCapacities(row) {
|
||||
return slotCapacityCount(row) > 0;
|
||||
}
|
||||
function slotCapacityCount(row) {
|
||||
return normalizeSlotCapacities(row?.slotCapacities).length;
|
||||
}
|
||||
function openSlotCapacityDialog(row) {
|
||||
currentSlotCapacities.value = normalizeSlotCapacities(row?.slotCapacities);
|
||||
slotCapacityVisible.value = true;
|
||||
}
|
||||
function formatSlotCapacity(capacity) {
|
||||
const value = Number(capacity);
|
||||
if (!value) return "不限制";
|
||||
return String(value);
|
||||
}
|
||||
function marketEnableText(v) {
|
||||
if (v === "UPPER") return "上架";
|
||||
if (v === "DOWN") return "下架";
|
||||
return "—";
|
||||
}
|
||||
function authFlagText(v) {
|
||||
if (v === "TOBEAUDITED") return "待审核";
|
||||
if (v === "PASS") return "通过";
|
||||
if (v === "REFUSE") return "拒绝";
|
||||
return v || "—";
|
||||
}
|
||||
function authFlagTagType(v) {
|
||||
if (v === "PASS") return "success";
|
||||
if (v === "TOBEAUDITED") return "warning";
|
||||
if (v === "REFUSE") return "danger";
|
||||
return "info";
|
||||
}
|
||||
function formatServiceModes(modes) {
|
||||
if (!modes || !modes.length) return "—";
|
||||
const map = {
|
||||
IN_STORE: "到店服务",
|
||||
ON_SITE: "上门服务",
|
||||
};
|
||||
return modes.map((m) => map[m] || m).join("、");
|
||||
}
|
||||
function formatWeekdays(weekdays) {
|
||||
if (!Array.isArray(weekdays) || !weekdays.length) return "";
|
||||
const map = {
|
||||
1: "周一",
|
||||
2: "周二",
|
||||
3: "周三",
|
||||
4: "周四",
|
||||
5: "周五",
|
||||
6: "周六",
|
||||
7: "周日",
|
||||
};
|
||||
return weekdays.map((d) => map[d] || d).join("、");
|
||||
}
|
||||
function handleViewGoodsPicture(url) {
|
||||
previewGoodsPicture.value = url;
|
||||
goodsPictureVisible.value = true;
|
||||
}
|
||||
function openAuditModal() {
|
||||
goodsAuditForm.auth_flag = 1;
|
||||
auditSubmitLoading.value = false;
|
||||
auditModalVisible.value = true;
|
||||
}
|
||||
function submitAudit() {
|
||||
if (!goods.value.id) return;
|
||||
auditSubmitLoading.value = true;
|
||||
const formData = new FormData();
|
||||
formData.append("goodsIds", goods.value.id);
|
||||
formData.append("authFlag", goodsAuditForm.auth_flag === 1 ? "PASS" : "REFUSE");
|
||||
authGoods(formData)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("审核成功");
|
||||
auditModalVisible.value = false;
|
||||
initGoods(goods.value.id);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
auditSubmitLoading.value = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -87,7 +87,7 @@
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form ref="formRef" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form-item label="品牌名称" prop="name">
|
||||
<el-input v-model="form.name" clearable style="width: 100%" />
|
||||
</el-form-item>
|
||||
@@ -110,7 +110,7 @@
|
||||
|
||||
<el-dialog v-model="picModelFlag" width="1200px" :show-close="true">
|
||||
<ossManage
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
@callback="callbackSelected"
|
||||
@@ -146,13 +146,14 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, onMounted, getCurrentInstance } from "vue";
|
||||
import {
|
||||
getManagerBrandPage,
|
||||
addBrand,
|
||||
updateBrand,
|
||||
disableBrand,
|
||||
delBrand,
|
||||
delBrand as delBrandApi,
|
||||
getCategoryTree,
|
||||
getBrandCategoryListData,
|
||||
saveBrandCategory,
|
||||
@@ -162,222 +163,234 @@ import { regular } from "@/utils";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import defaultPic from "@/assets/default.png";
|
||||
|
||||
export default {
|
||||
name: "brand",
|
||||
components: {
|
||||
ossManage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultPic,
|
||||
loading: true,
|
||||
modalType: 0,
|
||||
modalVisible: false,
|
||||
modalTitle: "",
|
||||
picModelFlag: false,
|
||||
categoryModalVisible: false,
|
||||
categoryModalTitle: "关联分类",
|
||||
categoryTreeLoading: false,
|
||||
categoryTreeData: [],
|
||||
categoryTreeKey: 0,
|
||||
categorySubmitLoading: false,
|
||||
currentBrandId: "",
|
||||
selectedCategoryIds: [],
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
},
|
||||
form: {
|
||||
name: "",
|
||||
logo: "",
|
||||
deleteFlag: "",
|
||||
},
|
||||
formValidate: {
|
||||
name: [regular.REQUIRED, regular.VARCHAR20],
|
||||
logo: [regular.REQUIRED, regular.URL200],
|
||||
},
|
||||
submitLoading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
openLogoPicker() {
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
this.form.logo = val.url;
|
||||
},
|
||||
buildCategoryTreeNodes(list) {
|
||||
if (!Array.isArray(list) || list.length === 0) return [];
|
||||
return list.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.name,
|
||||
children: this.buildCategoryTreeNodes(item.children || []),
|
||||
}));
|
||||
},
|
||||
async openCategoryModal(row) {
|
||||
this.currentBrandId = row.id;
|
||||
this.categoryModalTitle = "关联分类 - " + (row.name || "");
|
||||
this.categoryModalVisible = true;
|
||||
this.categoryTreeLoading = true;
|
||||
this.categoryTreeKey += 1;
|
||||
this.categoryTreeData = [];
|
||||
this.selectedCategoryIds = [];
|
||||
try {
|
||||
const [treeRes, bindRes] = await Promise.all([
|
||||
getCategoryTree(),
|
||||
getBrandCategoryListData(row.id),
|
||||
]);
|
||||
const selectedIds = Array.isArray(bindRes?.result)
|
||||
? bindRes.result
|
||||
.map((x) => (typeof x === "string" ? x : x && x.id))
|
||||
.filter(Boolean)
|
||||
: [];
|
||||
this.selectedCategoryIds = selectedIds;
|
||||
this.categoryTreeData =
|
||||
treeRes && treeRes.success
|
||||
? this.buildCategoryTreeNodes(treeRes.result || [])
|
||||
: [];
|
||||
} finally {
|
||||
this.categoryTreeLoading = false;
|
||||
defineOptions({ name: "brand" });
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const loading = ref(true);
|
||||
const modalType = ref(0);
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref("");
|
||||
const picModelFlag = ref(false);
|
||||
const categoryModalVisible = ref(false);
|
||||
const categoryModalTitle = ref("关联分类");
|
||||
const categoryTreeLoading = ref(false);
|
||||
const categoryTreeData = ref([]);
|
||||
const categoryTreeKey = ref(0);
|
||||
const categorySubmitLoading = ref(false);
|
||||
const currentBrandId = ref("");
|
||||
const selectedCategoryIds = ref([]);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
});
|
||||
const form = ref({
|
||||
name: "",
|
||||
logo: "",
|
||||
deleteFlag: "",
|
||||
});
|
||||
const formValidate = reactive({
|
||||
name: [regular.REQUIRED, regular.VARCHAR20],
|
||||
logo: [regular.REQUIRED, regular.URL200],
|
||||
});
|
||||
const submitLoading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
const formRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function openLogoPicker() {
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
form.value.logo = val.url;
|
||||
}
|
||||
|
||||
function buildCategoryTreeNodes(list) {
|
||||
if (!Array.isArray(list) || list.length === 0) return [];
|
||||
return list.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.name,
|
||||
children: buildCategoryTreeNodes(item.children || []),
|
||||
}));
|
||||
}
|
||||
|
||||
async function openCategoryModal(row) {
|
||||
currentBrandId.value = row.id;
|
||||
categoryModalTitle.value = "关联分类 - " + (row.name || "");
|
||||
categoryModalVisible.value = true;
|
||||
categoryTreeLoading.value = true;
|
||||
categoryTreeKey.value += 1;
|
||||
categoryTreeData.value = [];
|
||||
selectedCategoryIds.value = [];
|
||||
try {
|
||||
const [treeRes, bindRes] = await Promise.all([
|
||||
getCategoryTree(),
|
||||
getBrandCategoryListData(row.id),
|
||||
]);
|
||||
const selectedIds = Array.isArray(bindRes?.result)
|
||||
? bindRes.result
|
||||
.map((x) => (typeof x === "string" ? x : x && x.id))
|
||||
.filter(Boolean)
|
||||
: [];
|
||||
selectedCategoryIds.value = selectedIds;
|
||||
categoryTreeData.value =
|
||||
treeRes && treeRes.success
|
||||
? buildCategoryTreeNodes(treeRes.result || [])
|
||||
: [];
|
||||
} finally {
|
||||
categoryTreeLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function onCategoryTreeCheckChange(_data, checkedInfo) {
|
||||
selectedCategoryIds.value = checkedInfo?.checkedKeys || [];
|
||||
}
|
||||
|
||||
function submitBrandCategory() {
|
||||
if (!currentBrandId.value) return;
|
||||
categorySubmitLoading.value = true;
|
||||
saveBrandCategory(
|
||||
currentBrandId.value,
|
||||
(selectedCategoryIds.value || []).map((id) => String(id))
|
||||
)
|
||||
.then((res) => {
|
||||
categorySubmitLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
categoryModalVisible.value = false;
|
||||
}
|
||||
},
|
||||
onCategoryTreeCheckChange(_data, checkedInfo) {
|
||||
this.selectedCategoryIds = checkedInfo?.checkedKeys || [];
|
||||
},
|
||||
submitBrandCategory() {
|
||||
if (!this.currentBrandId) return;
|
||||
this.categorySubmitLoading = true;
|
||||
saveBrandCategory(
|
||||
this.currentBrandId,
|
||||
(this.selectedCategoryIds || []).map((id) => String(id))
|
||||
)
|
||||
.then((res) => {
|
||||
this.categorySubmitLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.categoryModalVisible = false;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.categorySubmitLoading = false;
|
||||
});
|
||||
},
|
||||
handleStatusSwitchChange(row, checked) {
|
||||
const disable = !checked;
|
||||
ElMessageBox.confirm(
|
||||
"您确认要" + (disable ? "禁用" : "启用") + "品牌 " + row.name + " ?",
|
||||
disable ? "确认禁用" : "确认启用",
|
||||
{ type: "warning" }
|
||||
).then(() => {
|
||||
return disableBrand(row.id, { disable }).then((res) => {
|
||||
})
|
||||
.catch(() => {
|
||||
categorySubmitLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleStatusSwitchChange(row, checked) {
|
||||
const disable = !checked;
|
||||
ElMessageBox.confirm(
|
||||
"您确认要" + (disable ? "禁用" : "启用") + "品牌 " + row.name + " ?",
|
||||
disable ? "确认禁用" : "确认启用",
|
||||
{ type: "warning" }
|
||||
).then(() => {
|
||||
return disableBrand(row.id, { disable }).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
}
|
||||
getDataList();
|
||||
});
|
||||
}).catch(() => {
|
||||
nextTick(() => proxy.$forceUpdate());
|
||||
});
|
||||
}
|
||||
|
||||
function delBrand(row) {
|
||||
ElMessageBox.confirm("您确认要删除 " + row.name + " ?", "确认删除", { type: "warning" }).then(() => {
|
||||
return delBrandApi(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("品牌删除成功!");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getManagerBrandPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleSubmit() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true;
|
||||
if (modalType.value === 0) {
|
||||
delete form.value.id;
|
||||
addBrand(form.value).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
this.getDataList();
|
||||
});
|
||||
}).catch(() => {
|
||||
this.$nextTick(() => this.$forceUpdate());
|
||||
});
|
||||
},
|
||||
delBrand(row) {
|
||||
ElMessageBox.confirm("您确认要删除 " + row.name + " ?", "确认删除", { type: "warning" }).then(() => {
|
||||
return delBrand(row.id).then((res) => {
|
||||
} else {
|
||||
updateBrand(form.value).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("品牌删除成功!");
|
||||
this.getDataList();
|
||||
ElMessage.success("操作成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getManagerBrandPage(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.submitLoading = true;
|
||||
if (this.modalType === 0) {
|
||||
delete this.form.id;
|
||||
addBrand(this.form).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateBrand(this.form).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
add() {
|
||||
this.modalType = 0;
|
||||
this.modalTitle = "添加";
|
||||
this.$refs.form?.resetFields();
|
||||
delete this.form.id;
|
||||
this.modalVisible = true;
|
||||
},
|
||||
edit(v) {
|
||||
this.modalType = 1;
|
||||
this.modalTitle = "编辑";
|
||||
this.$refs.form?.resetFields();
|
||||
for (const attr in v) {
|
||||
if (v[attr] === null) {
|
||||
v[attr] = "";
|
||||
}
|
||||
}
|
||||
const str = JSON.stringify(v);
|
||||
const data = JSON.parse(str);
|
||||
this.form = data;
|
||||
this.modalVisible = true;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function add() {
|
||||
modalType.value = 0;
|
||||
modalTitle.value = "添加";
|
||||
formRef.value?.resetFields();
|
||||
delete form.value.id;
|
||||
modalVisible.value = true;
|
||||
}
|
||||
|
||||
function edit(v) {
|
||||
modalType.value = 1;
|
||||
modalTitle.value = "编辑";
|
||||
formRef.value?.resetFields();
|
||||
for (const attr in v) {
|
||||
if (v[attr] === null) {
|
||||
v[attr] = "";
|
||||
}
|
||||
}
|
||||
const str = JSON.stringify(v);
|
||||
const data = JSON.parse(str);
|
||||
form.value = data;
|
||||
modalVisible.value = true;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="form" :model="formAdd" label-width="110px" :rules="formValidate">
|
||||
<el-form ref="formRef" :model="formAdd" label-width="110px" :rules="formValidate">
|
||||
<el-form-item v-if="showParent" label="上级分类" prop="parentId">
|
||||
{{ parentTitle }}
|
||||
<el-input v-model="formAdd.parentId" style="display: none" />
|
||||
@@ -106,7 +106,7 @@
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form ref="brandForm" :model="brandForm" label-width="100px">
|
||||
<el-form ref="brandFormRef" :model="brandForm" label-width="100px">
|
||||
<el-select v-model="brandForm.categoryBrands" filterable multiple style="width: 100%">
|
||||
<el-option v-for="item in brandWay" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
@@ -120,7 +120,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import {
|
||||
delCategory,
|
||||
disableCategory,
|
||||
@@ -128,241 +129,249 @@ import {
|
||||
getCategoryBrandListData,
|
||||
getCategoryTree,
|
||||
insertCategory,
|
||||
saveCategoryBrand,
|
||||
saveCategoryBrand as apiSaveCategoryBrand,
|
||||
updateCategory,
|
||||
} from "@/api/goods";
|
||||
import uploadPicInput from "@/components/lili/upload-pic-input";
|
||||
import { regular } from "@/utils";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "goods-category",
|
||||
components: {
|
||||
uploadPicInput,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
submitLoading: false,
|
||||
categoryList: [],
|
||||
loading: false,
|
||||
brands: [],
|
||||
categoryId: "",
|
||||
modalType: 0,
|
||||
modalVisible: false,
|
||||
modalBrandVisible: false,
|
||||
modalTitle: "",
|
||||
showParent: false,
|
||||
parentTitle: "",
|
||||
modalBrandTitle: "",
|
||||
formAdd: {
|
||||
parentId: "",
|
||||
name: "",
|
||||
image: "",
|
||||
sortOrder: 0,
|
||||
deleteFlag: false,
|
||||
commissionRate: 0,
|
||||
level: 0,
|
||||
},
|
||||
brandForm: {
|
||||
categoryBrands: [],
|
||||
},
|
||||
brandWay: [],
|
||||
formValidate: {
|
||||
commissionRate: [regular.REQUIRED, regular.INTEGER],
|
||||
name: [regular.REQUIRED, regular.VARCHAR20],
|
||||
sortOrder: [regular.REQUIRED, regular.INTEGER],
|
||||
},
|
||||
tableData: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
normalizeCategoryTree(list) {
|
||||
if (!Array.isArray(list) || list.length === 0) return;
|
||||
list.forEach((item) => {
|
||||
if (!item || typeof item !== "object") return;
|
||||
if (item.deleteFlag === 0) item.deleteFlag = false;
|
||||
else if (item.deleteFlag === 1) item.deleteFlag = true;
|
||||
else item.deleteFlag = !!item.deleteFlag;
|
||||
if (Array.isArray(item.children) && item.children.length) {
|
||||
this.normalizeCategoryTree(item.children);
|
||||
}
|
||||
});
|
||||
},
|
||||
onStatusSwitchChange(row, nextDeleteFlag) {
|
||||
const previousDeleteFlag = !nextDeleteFlag;
|
||||
const isClosing = nextDeleteFlag === true;
|
||||
ElMessageBox.confirm(
|
||||
`您是否要${isClosing ? "关闭" : "开启"}当前分类 ${row.name} 及其子分类?`,
|
||||
isClosing ? "确认关闭" : "确认开启",
|
||||
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
|
||||
).then(() => {
|
||||
row._statusLoading = true;
|
||||
return disableCategory(row.id, { enableOperations: isClosing ? true : 0 }).then((res) => {
|
||||
row._statusLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.getAllList();
|
||||
return;
|
||||
}
|
||||
row.deleteFlag = previousDeleteFlag;
|
||||
});
|
||||
}).catch(() => {
|
||||
row.deleteFlag = previousDeleteFlag;
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.getAllList();
|
||||
this.getBrandList();
|
||||
},
|
||||
getBrandList() {
|
||||
getBrandListData().then((res) => {
|
||||
this.brandWay = res;
|
||||
});
|
||||
},
|
||||
brandOperation(v) {
|
||||
getCategoryBrandListData(v.id).then((res) => {
|
||||
this.categoryId = v.id;
|
||||
this.modalBrandTitle = "品牌关联";
|
||||
this.brandForm.categoryBrands = res.result.map((item) => item.id);
|
||||
this.modalBrandVisible = true;
|
||||
});
|
||||
},
|
||||
saveCategoryBrand() {
|
||||
saveCategoryBrand(this.categoryId, this.brandForm).then((res) => {
|
||||
this.submitLoading = false;
|
||||
defineOptions({ name: "goods-category" });
|
||||
|
||||
const submitLoading = ref(false);
|
||||
const categoryList = ref([]);
|
||||
const loading = ref(false);
|
||||
const brands = ref([]);
|
||||
const categoryId = ref("");
|
||||
const modalType = ref(0);
|
||||
const modalVisible = ref(false);
|
||||
const modalBrandVisible = ref(false);
|
||||
const modalTitle = ref("");
|
||||
const showParent = ref(false);
|
||||
const parentTitle = ref("");
|
||||
const modalBrandTitle = ref("");
|
||||
const formAdd = reactive({
|
||||
parentId: "",
|
||||
name: "",
|
||||
image: "",
|
||||
sortOrder: 0,
|
||||
deleteFlag: false,
|
||||
commissionRate: 0,
|
||||
level: 0,
|
||||
});
|
||||
const brandForm = reactive({
|
||||
categoryBrands: [],
|
||||
});
|
||||
const brandWay = ref([]);
|
||||
const formValidate = reactive({
|
||||
commissionRate: [regular.REQUIRED, regular.INTEGER],
|
||||
name: [regular.REQUIRED, regular.VARCHAR20],
|
||||
sortOrder: [regular.REQUIRED, regular.INTEGER],
|
||||
});
|
||||
const tableData = ref([]);
|
||||
|
||||
const formRef = ref(null);
|
||||
const brandFormRef = ref(null);
|
||||
|
||||
function normalizeCategoryTree(list) {
|
||||
if (!Array.isArray(list) || list.length === 0) return;
|
||||
list.forEach((item) => {
|
||||
if (!item || typeof item !== "object") return;
|
||||
if (item.deleteFlag === 0) item.deleteFlag = false;
|
||||
else if (item.deleteFlag === 1) item.deleteFlag = true;
|
||||
else item.deleteFlag = !!item.deleteFlag;
|
||||
if (Array.isArray(item.children) && item.children.length) {
|
||||
normalizeCategoryTree(item.children);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onStatusSwitchChange(row, nextDeleteFlag) {
|
||||
const previousDeleteFlag = !nextDeleteFlag;
|
||||
const isClosing = nextDeleteFlag === true;
|
||||
ElMessageBox.confirm(
|
||||
`您是否要${isClosing ? "关闭" : "开启"}当前分类 ${row.name} 及其子分类?`,
|
||||
isClosing ? "确认关闭" : "确认开启",
|
||||
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
|
||||
).then(() => {
|
||||
row._statusLoading = true;
|
||||
return disableCategory(row.id, { enableOperations: isClosing ? true : 0 }).then((res) => {
|
||||
row._statusLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
getAllList();
|
||||
return;
|
||||
}
|
||||
row.deleteFlag = previousDeleteFlag;
|
||||
});
|
||||
}).catch(() => {
|
||||
row.deleteFlag = previousDeleteFlag;
|
||||
});
|
||||
}
|
||||
|
||||
function init() {
|
||||
getAllList();
|
||||
getBrandList();
|
||||
}
|
||||
|
||||
function getBrandList() {
|
||||
getBrandListData().then((res) => {
|
||||
brandWay.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
function brandOperation(v) {
|
||||
getCategoryBrandListData(v.id).then((res) => {
|
||||
categoryId.value = v.id;
|
||||
modalBrandTitle.value = "品牌关联";
|
||||
brandForm.categoryBrands = res.result.map((item) => item.id);
|
||||
modalBrandVisible.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
function saveCategoryBrand() {
|
||||
apiSaveCategoryBrand(categoryId.value, brandForm).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
modalBrandVisible.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addChildren(v) {
|
||||
modalType.value = 0;
|
||||
modalTitle.value = "添加子分类";
|
||||
parentTitle.value = v.name;
|
||||
formAdd.level = Number(v.level) + 1;
|
||||
formAdd.commissionRate = v.commissionRate;
|
||||
showParent.value = true;
|
||||
delete formAdd.id;
|
||||
formAdd.parentId = v.id;
|
||||
modalVisible.value = true;
|
||||
}
|
||||
|
||||
function edit(v) {
|
||||
modalType.value = 1;
|
||||
modalTitle.value = "编辑";
|
||||
formAdd.id = v.id;
|
||||
formAdd.name = v.name;
|
||||
formAdd.level = v.level;
|
||||
formAdd.parentId = v.parentId;
|
||||
formAdd.sortOrder = v.sortOrder;
|
||||
formAdd.commissionRate = v.commissionRate;
|
||||
formAdd.deleteFlag = v.deleteFlag;
|
||||
formAdd.image = v.image;
|
||||
showParent.value = false;
|
||||
modalVisible.value = true;
|
||||
}
|
||||
|
||||
function addParent() {
|
||||
modalType.value = 0;
|
||||
modalTitle.value = "添加一级分类";
|
||||
parentTitle.value = "顶级分类";
|
||||
showParent.value = true;
|
||||
formRef.value?.resetFields();
|
||||
delete formAdd.id;
|
||||
formAdd.parentId = 0;
|
||||
formAdd.level = 0;
|
||||
modalVisible.value = true;
|
||||
}
|
||||
|
||||
function Submit() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitLoading.value = true;
|
||||
if (modalType.value === 0) {
|
||||
delete formAdd.id;
|
||||
insertCategory(formAdd).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.modalBrandVisible = false;
|
||||
ElMessage.success("添加成功");
|
||||
getAllList();
|
||||
modalVisible.value = false;
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
});
|
||||
},
|
||||
addChildren(v) {
|
||||
this.modalType = 0;
|
||||
this.modalTitle = "添加子分类";
|
||||
this.parentTitle = v.name;
|
||||
this.formAdd.level = Number(v.level) + 1;
|
||||
this.formAdd.commissionRate = v.commissionRate;
|
||||
this.showParent = true;
|
||||
delete this.formAdd.id;
|
||||
this.formAdd.parentId = v.id;
|
||||
this.modalVisible = true;
|
||||
},
|
||||
edit(v) {
|
||||
this.modalType = 1;
|
||||
this.modalTitle = "编辑";
|
||||
this.formAdd.id = v.id;
|
||||
this.formAdd.name = v.name;
|
||||
this.formAdd.level = v.level;
|
||||
this.formAdd.parentId = v.parentId;
|
||||
this.formAdd.sortOrder = v.sortOrder;
|
||||
this.formAdd.commissionRate = v.commissionRate;
|
||||
this.formAdd.deleteFlag = v.deleteFlag;
|
||||
this.formAdd.image = v.image;
|
||||
this.showParent = false;
|
||||
this.modalVisible = true;
|
||||
},
|
||||
addParent() {
|
||||
this.modalType = 0;
|
||||
this.modalTitle = "添加一级分类";
|
||||
this.parentTitle = "顶级分类";
|
||||
this.showParent = true;
|
||||
this.$refs.form?.resetFields();
|
||||
delete this.formAdd.id;
|
||||
this.formAdd.parentId = 0;
|
||||
this.formAdd.level = 0;
|
||||
this.modalVisible = true;
|
||||
},
|
||||
Submit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitLoading = true;
|
||||
if (this.modalType === 0) {
|
||||
delete this.formAdd.id;
|
||||
insertCategory(this.formAdd).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
this.getAllList();
|
||||
this.modalVisible = false;
|
||||
this.$refs.form.resetFields();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateCategory(this.formAdd).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
this.getAllList();
|
||||
this.modalVisible = false;
|
||||
this.$refs.form.resetFields();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateCategory(formAdd).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
getAllList();
|
||||
modalVisible.value = false;
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
});
|
||||
},
|
||||
remove(v) {
|
||||
ElMessageBox.confirm("您确认要删除 " + v.name + " ?", "确认删除", { type: "warning" }).then(() => {
|
||||
return delCategory(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.getAllList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
getAllList() {
|
||||
this.loading = true;
|
||||
getCategoryTree()
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
localStorage.setItem("category", JSON.stringify(res.result));
|
||||
this.normalizeCategoryTree(res.result);
|
||||
this.categoryList = JSON.parse(JSON.stringify(res.result));
|
||||
this.tableData = JSON.parse(JSON.stringify(res.result));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
enable(v) {
|
||||
ElMessageBox.confirm(
|
||||
"您是否要启用当前分类 " + v.name + " 及其子分类?",
|
||||
"确认启用",
|
||||
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
|
||||
).then(() => {
|
||||
return disableCategory(v.id, { enableOperations: 0 }).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.getAllList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {
|
||||
this.getAllList();
|
||||
});
|
||||
},
|
||||
disable(v) {
|
||||
ElMessageBox.confirm(
|
||||
"您是否要禁用当前分类 " + v.name + " 及其子分类?",
|
||||
"确认禁用",
|
||||
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
|
||||
).then(() => {
|
||||
return disableCategory(v.id, { enableOperations: true }).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.getAllList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {
|
||||
this.getAllList();
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function remove(v) {
|
||||
ElMessageBox.confirm("您确认要删除 " + v.name + " ?", "确认删除", { type: "warning" }).then(() => {
|
||||
return delCategory(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
getAllList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
function getAllList() {
|
||||
loading.value = true;
|
||||
getCategoryTree()
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
localStorage.setItem("category", JSON.stringify(res.result));
|
||||
normalizeCategoryTree(res.result);
|
||||
categoryList.value = JSON.parse(JSON.stringify(res.result));
|
||||
tableData.value = JSON.parse(JSON.stringify(res.result));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function enable(v) {
|
||||
ElMessageBox.confirm(
|
||||
"您是否要启用当前分类 " + v.name + " 及其子分类?",
|
||||
"确认启用",
|
||||
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
|
||||
).then(() => {
|
||||
return disableCategory(v.id, { enableOperations: 0 }).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
getAllList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {
|
||||
getAllList();
|
||||
});
|
||||
}
|
||||
|
||||
function disable(v) {
|
||||
ElMessageBox.confirm(
|
||||
"您是否要禁用当前分类 " + v.name + " 及其子分类?",
|
||||
"确认禁用",
|
||||
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
|
||||
).then(() => {
|
||||
return disableCategory(v.id, { enableOperations: true }).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
getAllList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {
|
||||
getAllList();
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form ref="formRef" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form-item label="参数名称" prop="paramName">
|
||||
<el-input v-model="form.paramName" clearable style="width: 520px" />
|
||||
</el-form-item>
|
||||
@@ -92,7 +92,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, nextTick, onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import {
|
||||
getCategoryTree,
|
||||
getGoodsParamsDetail,
|
||||
@@ -102,6 +104,11 @@ import {
|
||||
import { regular } from "@/utils";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
defineOptions({ name: "parameterEdit" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const getOptionText = (value) => {
|
||||
if (value && typeof value === "object") return value.value;
|
||||
return value;
|
||||
@@ -213,228 +220,228 @@ const buildFormState = (source, parameterId) => {
|
||||
return formState;
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "parameterEdit",
|
||||
data() {
|
||||
return {
|
||||
submitLoading: false,
|
||||
modalType: 0,
|
||||
categoryModalVisible: false,
|
||||
categoryModalTitle: "关联分类",
|
||||
categoryTreeLoading: false,
|
||||
categoryTreeData: [],
|
||||
categoryTreeSource: [],
|
||||
categoryIdNameMap: {},
|
||||
categoryTreeKey: 0,
|
||||
selectedCategoryIds: [],
|
||||
form: {
|
||||
paramName: "",
|
||||
options: [{ value: "" }],
|
||||
required: 0,
|
||||
isIndex: 0,
|
||||
sort: 0,
|
||||
categoryIds: [],
|
||||
},
|
||||
formValidate: {
|
||||
paramName: [regular.REQUIRED, regular.VARCHAR5],
|
||||
options: [{ required: true, validator: validateOptions, trigger: "change" }],
|
||||
required: [{ required: true, validator: validateRadioRequired("请选择是否必填"), trigger: "change" }],
|
||||
isIndex: [{ required: true, validator: validateRadioRequired("请选择是否索引"), trigger: "change" }],
|
||||
sort: [regular.REQUIRED, regular.INTEGER],
|
||||
categoryIds: [
|
||||
{
|
||||
type: "array",
|
||||
required: true,
|
||||
min: 1,
|
||||
message: "请选择关联分类",
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
id() {
|
||||
return this.$route.query && this.$route.query.id ? String(this.$route.query.id) : "";
|
||||
const formRef = ref(null);
|
||||
const categoryTree = ref(null);
|
||||
|
||||
const submitLoading = ref(false);
|
||||
const modalType = ref(0);
|
||||
const categoryModalVisible = ref(false);
|
||||
const categoryModalTitle = ref("关联分类");
|
||||
const categoryTreeLoading = ref(false);
|
||||
const categoryTreeData = ref([]);
|
||||
const categoryTreeSource = ref([]);
|
||||
const categoryIdNameMap = ref({});
|
||||
const categoryTreeKey = ref(0);
|
||||
const selectedCategoryIds = ref([]);
|
||||
const form = ref({
|
||||
paramName: "",
|
||||
options: [{ value: "" }],
|
||||
required: 0,
|
||||
isIndex: 0,
|
||||
sort: 0,
|
||||
categoryIds: [],
|
||||
});
|
||||
const formValidate = reactive({
|
||||
paramName: [regular.REQUIRED, regular.VARCHAR5],
|
||||
options: [{ required: true, validator: validateOptions, trigger: "change" }],
|
||||
required: [{ required: true, validator: validateRadioRequired("请选择是否必填"), trigger: "change" }],
|
||||
isIndex: [{ required: true, validator: validateRadioRequired("请选择是否索引"), trigger: "change" }],
|
||||
sort: [regular.REQUIRED, regular.INTEGER],
|
||||
categoryIds: [
|
||||
{
|
||||
type: "array",
|
||||
required: true,
|
||||
min: 1,
|
||||
message: "请选择关联分类",
|
||||
trigger: "change",
|
||||
},
|
||||
selectedCategoryNamesText() {
|
||||
if (!Array.isArray(this.categoryTreeSource) || this.categoryTreeSource.length === 0) return "";
|
||||
const selectedSet = new Set(toStringArray(this.selectedCategoryIds));
|
||||
const isSelected = (node) => {
|
||||
if (!node) return false;
|
||||
const nodeId = node.id !== undefined && node.id !== null ? String(node.id) : "";
|
||||
if (nodeId && selectedSet.has(nodeId)) return true;
|
||||
const children = Array.isArray(node.children) ? node.children : [];
|
||||
if (children.length === 0) return false;
|
||||
return children.every(isSelected);
|
||||
};
|
||||
const collect = (node, out) => {
|
||||
if (!node) return;
|
||||
if (isSelected(node)) {
|
||||
if (node.name) out.push(node.name);
|
||||
return;
|
||||
}
|
||||
const children = Array.isArray(node.children) ? node.children : [];
|
||||
children.forEach((c) => collect(c, out));
|
||||
};
|
||||
const names = [];
|
||||
this.categoryTreeSource.forEach((n) => collect(n, names));
|
||||
return names.join(",");
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
"form.required"(val) {
|
||||
const n = normalize01(val, 0);
|
||||
if (val !== n) this.form.required = n;
|
||||
},
|
||||
"form.isIndex"(val) {
|
||||
const n = normalize01(val, 0);
|
||||
if (val !== n) this.form.isIndex = n;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
this.$router.push({ name: "goods-parameter" });
|
||||
},
|
||||
syncCategoryIds(ids) {
|
||||
const categoryIds = toStringArray(ids);
|
||||
this.selectedCategoryIds = categoryIds;
|
||||
if (!this.form) this.form = {};
|
||||
this.form.categoryIds = categoryIds;
|
||||
},
|
||||
touchOptionsValidate() {
|
||||
this.$nextTick(() => {
|
||||
const form = this.$refs.form;
|
||||
if (form && typeof form.validateField === "function") {
|
||||
// validateField rejects on failure; catch to avoid dev-server [object Object] overlay
|
||||
form.validateField("options", () => {});
|
||||
}
|
||||
});
|
||||
},
|
||||
addOptionRow() {
|
||||
if (!Array.isArray(this.form.options)) this.form.options = [];
|
||||
this.form.options.push({ value: "" });
|
||||
},
|
||||
removeOptionRow(index) {
|
||||
if (!Array.isArray(this.form.options)) this.form.options = [];
|
||||
this.form.options.splice(index, 1);
|
||||
this.touchOptionsValidate();
|
||||
},
|
||||
buildCategoryTreeNodes(list) {
|
||||
if (!Array.isArray(list) || list.length === 0) return [];
|
||||
return list.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.name,
|
||||
children: this.buildCategoryTreeNodes(item.children || []),
|
||||
}));
|
||||
},
|
||||
async loadDetail(parameterId) {
|
||||
if (!parameterId) return;
|
||||
const res = await getGoodsParamsDetail(parameterId).catch(() => null);
|
||||
if (!(res && res.success && res.result)) return;
|
||||
const dto = res.result;
|
||||
this.form = buildFormState(dto, parameterId);
|
||||
this.selectedCategoryIds = [...this.form.categoryIds];
|
||||
},
|
||||
async openCategoryModal() {
|
||||
this.categoryModalVisible = true;
|
||||
this.categoryTreeLoading = true;
|
||||
this.categoryTreeKey += 1;
|
||||
],
|
||||
});
|
||||
|
||||
const id = computed(() =>
|
||||
route.query && route.query.id ? String(route.query.id) : ""
|
||||
);
|
||||
const selectedCategoryNamesText = computed(() => {
|
||||
if (!Array.isArray(categoryTreeSource.value) || categoryTreeSource.value.length === 0) return "";
|
||||
const selectedSet = new Set(toStringArray(selectedCategoryIds.value));
|
||||
const isSelected = (node) => {
|
||||
if (!node) return false;
|
||||
const nodeId = node.id !== undefined && node.id !== null ? String(node.id) : "";
|
||||
if (nodeId && selectedSet.has(nodeId)) return true;
|
||||
const children = Array.isArray(node.children) ? node.children : [];
|
||||
if (children.length === 0) return false;
|
||||
return children.every(isSelected);
|
||||
};
|
||||
const collect = (node, out) => {
|
||||
if (!node) return;
|
||||
if (isSelected(node)) {
|
||||
if (node.name) out.push(node.name);
|
||||
return;
|
||||
}
|
||||
const children = Array.isArray(node.children) ? node.children : [];
|
||||
children.forEach((c) => collect(c, out));
|
||||
};
|
||||
const names = [];
|
||||
categoryTreeSource.value.forEach((n) => collect(n, names));
|
||||
return names.join(",");
|
||||
});
|
||||
|
||||
watch(
|
||||
() => form.value.required,
|
||||
(val) => {
|
||||
const n = normalize01(val, 0);
|
||||
if (val !== n) form.value.required = n;
|
||||
}
|
||||
);
|
||||
watch(
|
||||
() => form.value.isIndex,
|
||||
(val) => {
|
||||
const n = normalize01(val, 0);
|
||||
if (val !== n) form.value.isIndex = n;
|
||||
}
|
||||
);
|
||||
|
||||
function back() {
|
||||
router.push({ name: "goods-parameter" });
|
||||
}
|
||||
function syncCategoryIds(ids) {
|
||||
const categoryIds = toStringArray(ids);
|
||||
selectedCategoryIds.value = categoryIds;
|
||||
if (!form.value) form.value = {};
|
||||
form.value.categoryIds = categoryIds;
|
||||
}
|
||||
function touchOptionsValidate() {
|
||||
nextTick(() => {
|
||||
const formEl = formRef.value;
|
||||
if (formEl && typeof formEl.validateField === "function") {
|
||||
// validateField rejects on failure; catch to avoid dev-server [object Object] overlay
|
||||
formEl.validateField("options", () => {});
|
||||
}
|
||||
});
|
||||
}
|
||||
function addOptionRow() {
|
||||
if (!Array.isArray(form.value.options)) form.value.options = [];
|
||||
form.value.options.push({ value: "" });
|
||||
}
|
||||
function removeOptionRow(index) {
|
||||
if (!Array.isArray(form.value.options)) form.value.options = [];
|
||||
form.value.options.splice(index, 1);
|
||||
touchOptionsValidate();
|
||||
}
|
||||
function buildCategoryTreeNodes(list) {
|
||||
if (!Array.isArray(list) || list.length === 0) return [];
|
||||
return list.map((item) => ({
|
||||
id: item.id,
|
||||
title: item.name,
|
||||
children: buildCategoryTreeNodes(item.children || []),
|
||||
}));
|
||||
}
|
||||
async function loadDetail(parameterId) {
|
||||
if (!parameterId) return;
|
||||
const res = await getGoodsParamsDetail(parameterId).catch(() => null);
|
||||
if (!(res && res.success && res.result)) return;
|
||||
const dto = res.result;
|
||||
form.value = buildFormState(dto, parameterId);
|
||||
selectedCategoryIds.value = [...form.value.categoryIds];
|
||||
}
|
||||
async function openCategoryModal() {
|
||||
categoryModalVisible.value = true;
|
||||
categoryTreeLoading.value = true;
|
||||
categoryTreeKey.value += 1;
|
||||
try {
|
||||
if (!Array.isArray(categoryTreeSource.value) || categoryTreeSource.value.length === 0) {
|
||||
const treeRes = await getCategoryTree();
|
||||
categoryTreeSource.value = treeRes && treeRes.success ? treeRes.result || [] : [];
|
||||
const map = {};
|
||||
buildCategoryIdNameMap(categoryTreeSource.value, map);
|
||||
categoryIdNameMap.value = map;
|
||||
}
|
||||
categoryTreeData.value = buildCategoryTreeNodes(categoryTreeSource.value || []);
|
||||
} finally {
|
||||
categoryTreeLoading.value = false;
|
||||
}
|
||||
}
|
||||
function onCategoryTreeCheckChange(_data, checkedInfo) {
|
||||
syncCategoryIds(checkedInfo?.checkedKeys || []);
|
||||
nextTick(() => {
|
||||
const formEl = formRef.value;
|
||||
if (formEl && typeof formEl.validateField === "function") {
|
||||
formEl.validateField("categoryIds", () => {});
|
||||
}
|
||||
});
|
||||
}
|
||||
function initForm() {
|
||||
if (id.value) {
|
||||
modalType.value = 1;
|
||||
const cached = window.sessionStorage.getItem(cacheKey(id.value));
|
||||
if (cached) {
|
||||
try {
|
||||
if (!Array.isArray(this.categoryTreeSource) || this.categoryTreeSource.length === 0) {
|
||||
const treeRes = await getCategoryTree();
|
||||
this.categoryTreeSource = treeRes && treeRes.success ? treeRes.result || [] : [];
|
||||
const map = {};
|
||||
buildCategoryIdNameMap(this.categoryTreeSource, map);
|
||||
this.categoryIdNameMap = map;
|
||||
}
|
||||
this.categoryTreeData = this.buildCategoryTreeNodes(this.categoryTreeSource || []);
|
||||
} finally {
|
||||
this.categoryTreeLoading = false;
|
||||
const row = JSON.parse(cached);
|
||||
form.value = buildFormState(row, row.id);
|
||||
selectedCategoryIds.value = [...form.value.categoryIds];
|
||||
} catch (e) {
|
||||
modalType.value = 0;
|
||||
}
|
||||
},
|
||||
onCategoryTreeCheckChange(_data, checkedInfo) {
|
||||
this.syncCategoryIds(checkedInfo?.checkedKeys || []);
|
||||
this.$nextTick(() => {
|
||||
const form = this.$refs.form;
|
||||
if (form && typeof form.validateField === "function") {
|
||||
form.validateField("categoryIds", () => {});
|
||||
}
|
||||
});
|
||||
},
|
||||
initForm() {
|
||||
if (this.id) {
|
||||
this.modalType = 1;
|
||||
const cached = window.sessionStorage.getItem(cacheKey(this.id));
|
||||
if (cached) {
|
||||
try {
|
||||
const row = JSON.parse(cached);
|
||||
this.form = buildFormState(row, row.id);
|
||||
this.selectedCategoryIds = [...this.form.categoryIds];
|
||||
} catch (e) {
|
||||
this.modalType = 0;
|
||||
}
|
||||
}
|
||||
this.loadDetail(String(this.id));
|
||||
} else {
|
||||
this.modalType = 0;
|
||||
if (!Array.isArray(this.form.options) || this.form.options.length === 0) {
|
||||
this.form.options = [{ value: "" }];
|
||||
}
|
||||
this.syncCategoryIds([]);
|
||||
}
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitLoading = true;
|
||||
const options = normalizeOptions(this.form.options);
|
||||
const categoryIds = toStringArray(this.form.categoryIds);
|
||||
const payload = {
|
||||
...this.form,
|
||||
options: options.join(","),
|
||||
required: Number(this.form.required),
|
||||
isIndex: Number(this.form.isIndex),
|
||||
sort: Number(this.form.sort || 0),
|
||||
categoryParameterList: categoryIds.map((categoryId) => ({ categoryId })),
|
||||
};
|
||||
if (this.modalType === 0) {
|
||||
delete payload.id;
|
||||
insertGoodsParams(buildSpringFormPayload(payload))
|
||||
.then((res) => {
|
||||
if (!(res && res.success)) return;
|
||||
ElMessage.success("操作成功");
|
||||
this.back();
|
||||
})
|
||||
.finally(() => {
|
||||
this.submitLoading = false;
|
||||
});
|
||||
} else {
|
||||
updateGoodsParams(buildSpringFormPayload(payload))
|
||||
.then((res) => {
|
||||
if (!(res && res.success)) return;
|
||||
ElMessage.success("操作成功");
|
||||
this.back();
|
||||
})
|
||||
.finally(() => {
|
||||
this.submitLoading = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
getCategoryTree().then((res) => {
|
||||
if (res && res.success) {
|
||||
this.categoryTreeSource = res.result || [];
|
||||
const map = {};
|
||||
buildCategoryIdNameMap(this.categoryTreeSource, map);
|
||||
this.categoryIdNameMap = map;
|
||||
}
|
||||
});
|
||||
this.initForm();
|
||||
},
|
||||
};
|
||||
}
|
||||
loadDetail(String(id.value));
|
||||
} else {
|
||||
modalType.value = 0;
|
||||
if (!Array.isArray(form.value.options) || form.value.options.length === 0) {
|
||||
form.value.options = [{ value: "" }];
|
||||
}
|
||||
syncCategoryIds([]);
|
||||
}
|
||||
}
|
||||
function handleSubmit() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitLoading.value = true;
|
||||
const options = normalizeOptions(form.value.options);
|
||||
const categoryIds = toStringArray(form.value.categoryIds);
|
||||
const payload = {
|
||||
...form.value,
|
||||
options: options.join(","),
|
||||
required: Number(form.value.required),
|
||||
isIndex: Number(form.value.isIndex),
|
||||
sort: Number(form.value.sort || 0),
|
||||
categoryParameterList: categoryIds.map((categoryId) => ({ categoryId })),
|
||||
};
|
||||
if (modalType.value === 0) {
|
||||
delete payload.id;
|
||||
insertGoodsParams(buildSpringFormPayload(payload))
|
||||
.then((res) => {
|
||||
if (!(res && res.success)) return;
|
||||
ElMessage.success("操作成功");
|
||||
back();
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
} else {
|
||||
updateGoodsParams(buildSpringFormPayload(payload))
|
||||
.then((res) => {
|
||||
if (!(res && res.success)) return;
|
||||
ElMessage.success("操作成功");
|
||||
back();
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getCategoryTree().then((res) => {
|
||||
if (res && res.success) {
|
||||
categoryTreeSource.value = res.result || [];
|
||||
const map = {};
|
||||
buildCategoryIdNameMap(categoryTreeSource.value, map);
|
||||
categoryIdNameMap.value = map;
|
||||
}
|
||||
});
|
||||
initForm();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -27,7 +27,7 @@
|
||||
<div class="operation padding-row">
|
||||
<el-button type="primary" @click="goAdd">添加</el-button>
|
||||
</div>
|
||||
<el-table v-loading="loading" border :data="data" ref="table" style="width: 100%">
|
||||
<el-table v-loading="loading" border :data="data" ref="tableRef" style="width: 100%">
|
||||
<el-table-column prop="paramName" label="参数名称" width="300" />
|
||||
<el-table-column prop="options" label="参数值" min-width="260" show-overflow-tooltip />
|
||||
<el-table-column label="必填" width="300" align="center">
|
||||
@@ -65,83 +65,93 @@
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { deleteParams, getGoodsParamsPage } from "@/api/goods";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "categoryParams",
|
||||
data() {
|
||||
return {
|
||||
submitLoading: false,
|
||||
loading: true,
|
||||
total: 0,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
paramName: "",
|
||||
},
|
||||
data: [],
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
isTruthyFlag(val) {
|
||||
return val === 1 || val === "1" || val === true;
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getGoodsParamsPage(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res && res.success) {
|
||||
this.data = (res.result && res.result.records) || [];
|
||||
this.total = (res.result && res.result.total) || 0;
|
||||
}
|
||||
});
|
||||
},
|
||||
goAdd() {
|
||||
this.$router.push({ name: "goods-parameter-edit" });
|
||||
},
|
||||
goEdit(row) {
|
||||
if (!row || !row.id) return;
|
||||
try {
|
||||
window.sessionStorage.setItem(`goods-parameter-edit:${row.id}`, JSON.stringify(row));
|
||||
} catch (e) {}
|
||||
this.$router.push({ name: "goods-parameter-edit", query: { id: row.id } });
|
||||
},
|
||||
remove(row) {
|
||||
ElMessageBox.confirm("您确认要删除 " + (row.paramName || "") + " ?", "确认删除", { type: "warning" }).then(() => {
|
||||
return deleteParams(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "categoryParams" });
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const submitLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
const total = ref(0);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
paramName: "",
|
||||
});
|
||||
const data = ref([]);
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
const tableRef = ref(null);
|
||||
|
||||
function isTruthyFlag(val) {
|
||||
return val === 1 || val === "1" || val === true;
|
||||
}
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getGoodsParamsPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res && res.success) {
|
||||
data.value = (res.result && res.result.records) || [];
|
||||
total.value = (res.result && res.result.total) || 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function goAdd() {
|
||||
router.push({ name: "goods-parameter-edit" });
|
||||
}
|
||||
|
||||
function goEdit(row) {
|
||||
if (!row || !row.id) return;
|
||||
try {
|
||||
window.sessionStorage.setItem(`goods-parameter-edit:${row.id}`, JSON.stringify(row));
|
||||
} catch (e) {}
|
||||
router.push({ name: "goods-parameter-edit", query: { id: row.id } });
|
||||
}
|
||||
|
||||
function remove(row) {
|
||||
ElMessageBox.confirm("您确认要删除 " + (row.paramName || "") + " ?", "确认删除", { type: "warning" }).then(() => {
|
||||
return deleteParams(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.link-text {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -170,145 +170,144 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { nextTick, onMounted, ref } from "vue";
|
||||
import * as API_Member from "@/api/member";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "goods-review",
|
||||
data() {
|
||||
return {
|
||||
infoData: {},
|
||||
infoFlag: false,
|
||||
infoTitle: "",
|
||||
currentReviewId: "",
|
||||
detailStatusReady: false,
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
goodsName: "",
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
gradeTagType(grade) {
|
||||
if (grade === "GOOD") return "success";
|
||||
if (grade === "MODERATE") return "warning";
|
||||
return "danger";
|
||||
},
|
||||
gradeText(grade) {
|
||||
if (grade === "GOOD") return "好评";
|
||||
if (grade === "MODERATE") return "中评";
|
||||
return "差评";
|
||||
},
|
||||
changeSwitchView(status) {
|
||||
if (!this.detailStatusReady || !this.currentReviewId) {
|
||||
return;
|
||||
defineOptions({ name: "goods-review" });
|
||||
|
||||
// 模板中的 el-form ref(原 ref="searchForm",为避免与数据 searchForm 同名互相覆盖而改名),script 内未使用
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
const infoData = ref({});
|
||||
const infoFlag = ref(false);
|
||||
const infoTitle = ref("");
|
||||
const currentReviewId = ref("");
|
||||
const detailStatusReady = ref(false);
|
||||
const loading = ref(true);
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
goodsName: "",
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
function gradeTagType(grade) {
|
||||
if (grade === "GOOD") return "success";
|
||||
if (grade === "MODERATE") return "warning";
|
||||
return "danger";
|
||||
}
|
||||
function gradeText(grade) {
|
||||
if (grade === "GOOD") return "好评";
|
||||
if (grade === "MODERATE") return "中评";
|
||||
return "差评";
|
||||
}
|
||||
function changeSwitchView(status) {
|
||||
if (!detailStatusReady.value || !currentReviewId.value) {
|
||||
return;
|
||||
}
|
||||
API_Member.updateMemberReview(currentReviewId.value, { status }).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("修改成功!");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.value.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.value.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.value.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function changeSwitch(row) {
|
||||
if (!row || !row.id) {
|
||||
return;
|
||||
}
|
||||
API_Member.updateMemberReview(row.id, { status: row.status }).then((res) => {
|
||||
if (res.success) {
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}
|
||||
function updateTop(v) {
|
||||
const top = !v.top;
|
||||
API_Member.updateMemberReviewTop(v.id, { top }).then((res) => {
|
||||
if (res.success) {
|
||||
data.value = data.value.map((item) => {
|
||||
if (item.goodsId !== v.goodsId) {
|
||||
return item;
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
top: item.id === v.id ? top : false,
|
||||
};
|
||||
});
|
||||
ElMessage.success(top ? "置顶成功!" : "取消置顶成功!");
|
||||
init();
|
||||
}
|
||||
});
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Member.getMemberReview(searchForm.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
API_Member.updateMemberReview(this.currentReviewId, { status }).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("修改成功!");
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function info(v) {
|
||||
currentReviewId.value = v.id;
|
||||
detailStatusReady.value = false;
|
||||
infoData.value = {};
|
||||
infoFlag.value = true;
|
||||
infoTitle.value = `用户${v.memberName}的评价详情`;
|
||||
API_Member.getMemberInfoReview(v.id).then((res) => {
|
||||
if (res.result) {
|
||||
infoData.value = res.result;
|
||||
nextTick(() => {
|
||||
detailStatusReady.value = true;
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
changeSwitch(row) {
|
||||
if (!row || !row.id) {
|
||||
return;
|
||||
}
|
||||
API_Member.updateMemberReview(row.id, { status: row.status }).then((res) => {
|
||||
if (res.success) {
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
updateTop(v) {
|
||||
const top = !v.top;
|
||||
API_Member.updateMemberReviewTop(v.id, { top }).then((res) => {
|
||||
if (res.success) {
|
||||
this.data = this.data.map((item) => {
|
||||
if (item.goodsId !== v.goodsId) {
|
||||
return item;
|
||||
}
|
||||
return {
|
||||
...item,
|
||||
top: item.id === v.id ? top : false,
|
||||
};
|
||||
});
|
||||
ElMessage.success(top ? "置顶成功!" : "取消置顶成功!");
|
||||
this.init();
|
||||
}
|
||||
});
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
API_Member.getMemberReview(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
info(v) {
|
||||
this.currentReviewId = v.id;
|
||||
this.detailStatusReady = false;
|
||||
this.infoData = {};
|
||||
this.infoFlag = true;
|
||||
this.infoTitle = `用户${v.memberName}的评价详情`;
|
||||
API_Member.getMemberInfoReview(v.id).then((res) => {
|
||||
if (res.result) {
|
||||
this.infoData = res.result;
|
||||
this.$nextTick(() => {
|
||||
this.detailStatusReady = true;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
onInfoDialogClosed() {
|
||||
this.detailStatusReady = false;
|
||||
this.currentReviewId = "";
|
||||
this.infoData = {};
|
||||
},
|
||||
remove(v) {
|
||||
ElMessageBox.confirm("您确认要删除客户" + v.memberName + "的评论?", "确认删除", { type: "warning" }).then(() => {
|
||||
return API_Member.delMemberReview(v.id).then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
this.init();
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
function onInfoDialogClosed() {
|
||||
detailStatusReady.value = false;
|
||||
currentReviewId.value = "";
|
||||
infoData.value = {};
|
||||
}
|
||||
function remove(v) {
|
||||
ElMessageBox.confirm("您确认要删除客户" + v.memberName + "的评论?", "确认删除", { type: "warning" }).then(() => {
|
||||
return API_Member.delMemberReview(v.id).then(() => {
|
||||
ElMessage.success("修改成功");
|
||||
init();
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="addFlag" title="添加商品分组" width="500px" :close-on-click-modal="false" destroy-on-close>
|
||||
<el-form ref="addForm" :model="formAdd" :rules="rulesAdd" label-width="90px">
|
||||
<el-form ref="addFormRef" :model="formAdd" :rules="rulesAdd" label-width="90px">
|
||||
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
|
||||
<el-input v-model="formAdd.groupName" maxlength="30" placeholder="请输入分组名称" />
|
||||
</el-form-item>
|
||||
@@ -51,7 +51,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="editFlag" title="编辑商品分组" width="500px" :close-on-click-modal="false" destroy-on-close>
|
||||
<el-form ref="editForm" :model="formEdit" :rules="rulesEdit" label-width="90px">
|
||||
<el-form ref="editFormRef" :model="formEdit" :rules="rulesEdit" label-width="90px">
|
||||
<el-input v-model="formEdit.id" style="display: none" />
|
||||
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
|
||||
<el-input v-model="formEdit.groupName" maxlength="30" placeholder="请输入分组名称" />
|
||||
@@ -68,166 +68,173 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, onMounted } from "vue";
|
||||
import * as API_Goods from "@/api/goods.js";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "goodsGroup",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
addFlag: false,
|
||||
editFlag: false,
|
||||
submitAddLoading: false,
|
||||
submitEditLoading: false,
|
||||
formAdd: {
|
||||
groupName: "",
|
||||
description: "",
|
||||
},
|
||||
formEdit: {
|
||||
id: "",
|
||||
groupName: "",
|
||||
description: "",
|
||||
},
|
||||
rulesAdd: {
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
},
|
||||
rulesEdit: {
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getData();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getData();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getData();
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
API_Goods.getGoodsGroupByPage(this.searchForm)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res && res.success && res.result) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
ElMessage.error("加载商品分组失败");
|
||||
});
|
||||
},
|
||||
openAdd() {
|
||||
this.addFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.addForm) this.$refs.addForm.resetFields();
|
||||
this.formAdd = { groupName: "", description: "" };
|
||||
defineOptions({ name: "goodsGroup" });
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const addFlag = ref(false);
|
||||
const editFlag = ref(false);
|
||||
const submitAddLoading = ref(false);
|
||||
const submitEditLoading = ref(false);
|
||||
const formAdd = ref({
|
||||
groupName: "",
|
||||
description: "",
|
||||
});
|
||||
const formEdit = ref({
|
||||
id: "",
|
||||
groupName: "",
|
||||
description: "",
|
||||
});
|
||||
const rulesAdd = reactive({
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
});
|
||||
const rulesEdit = reactive({
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
});
|
||||
|
||||
const addFormRef = ref(null);
|
||||
const editFormRef = ref(null);
|
||||
|
||||
function init() {
|
||||
getData();
|
||||
}
|
||||
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getData();
|
||||
}
|
||||
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getData();
|
||||
}
|
||||
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
API_Goods.getGoodsGroupByPage(searchForm)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res && res.success && res.result) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
ElMessage.error("加载商品分组失败");
|
||||
});
|
||||
}
|
||||
|
||||
function openAdd() {
|
||||
addFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (addFormRef.value) addFormRef.value.resetFields();
|
||||
formAdd.value = { groupName: "", description: "" };
|
||||
});
|
||||
}
|
||||
|
||||
function submitAdd() {
|
||||
addFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitAddLoading.value = true;
|
||||
API_Goods.addGoodsGroup(formAdd.value)
|
||||
.then((res) => {
|
||||
submitAddLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
addFlag.value = false;
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
submitAddLoading.value = false;
|
||||
ElMessage.error("添加失败,请检查权限或后端接口");
|
||||
});
|
||||
},
|
||||
submitAdd() {
|
||||
this.$refs.addForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitAddLoading = true;
|
||||
API_Goods.addGoodsGroup(this.formAdd)
|
||||
.then((res) => {
|
||||
this.submitAddLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
this.addFlag = false;
|
||||
this.getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.submitAddLoading = false;
|
||||
ElMessage.error("添加失败,请检查权限或后端接口");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function openEdit(row) {
|
||||
editFlag.value = true;
|
||||
submitEditLoading.value = false;
|
||||
API_Goods.getGoodsGroup(row.id)
|
||||
.then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
formEdit.value = {
|
||||
id: res.result.id,
|
||||
groupName: res.result.groupName || "",
|
||||
description: res.result.description || "",
|
||||
};
|
||||
} else {
|
||||
formEdit.value = {
|
||||
id: row.id,
|
||||
groupName: row.groupName || "",
|
||||
description: row.description || "",
|
||||
};
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("获取分组详情失败");
|
||||
});
|
||||
}
|
||||
|
||||
function submitEdit() {
|
||||
editFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitEditLoading.value = true;
|
||||
const { id, groupName, description } = formEdit.value;
|
||||
API_Goods.updateGoodsGroup(id, { id, groupName, description })
|
||||
.then((res) => {
|
||||
submitEditLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
editFlag.value = false;
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
submitEditLoading.value = false;
|
||||
ElMessage.error("修改失败,请检查权限或后端接口");
|
||||
});
|
||||
},
|
||||
openEdit(row) {
|
||||
this.editFlag = true;
|
||||
this.submitEditLoading = false;
|
||||
API_Goods.getGoodsGroup(row.id)
|
||||
.then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
this.formEdit = {
|
||||
id: res.result.id,
|
||||
groupName: res.result.groupName || "",
|
||||
description: res.result.description || "",
|
||||
};
|
||||
} else {
|
||||
this.formEdit = {
|
||||
id: row.id,
|
||||
groupName: row.groupName || "",
|
||||
description: row.description || "",
|
||||
};
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("获取分组详情失败");
|
||||
});
|
||||
},
|
||||
submitEdit() {
|
||||
this.$refs.editForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitEditLoading = true;
|
||||
const { id, groupName, description } = this.formEdit;
|
||||
API_Goods.updateGoodsGroup(id, { id, groupName, description })
|
||||
.then((res) => {
|
||||
this.submitEditLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
this.editFlag = false;
|
||||
this.getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.submitEditLoading = false;
|
||||
ElMessage.error("修改失败,请检查权限或后端接口");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function remove(row) {
|
||||
ElMessageBox.confirm("确定删除该分组?", "提示", { type: "warning" }).then(() => {
|
||||
return API_Goods.deleteGoodsGroup(row.id)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("删除失败,请检查权限或后端接口");
|
||||
});
|
||||
},
|
||||
remove(row) {
|
||||
ElMessageBox.confirm("确定删除该分组?", "提示", { type: "warning" }).then(() => {
|
||||
return API_Goods.deleteGoodsGroup(row.id)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
this.getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("删除失败,请检查权限或后端接口");
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -68,7 +68,7 @@
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
:data="data"
|
||||
class="mt_10"
|
||||
@@ -208,7 +208,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import {
|
||||
batchUpdateGoodsSkuVirtualSales,
|
||||
getGoodsSkuData,
|
||||
@@ -225,223 +226,222 @@ const VIRTUAL_SALES_FIELDS = [
|
||||
"salesVolume",
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "goodsVirtualSales",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
editModalVisible: false,
|
||||
modalSubmitting: false,
|
||||
modalMode: "single",
|
||||
currentSku: null,
|
||||
currentIndex: -1,
|
||||
selectedRows: [],
|
||||
editForm: {
|
||||
virtualSales: 0,
|
||||
},
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
goodsName: "",
|
||||
goodsId: "",
|
||||
sn: "",
|
||||
storeName: "",
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleReset() {
|
||||
this.searchForm = {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
goodsName: "",
|
||||
goodsId: "",
|
||||
sn: "",
|
||||
storeName: "",
|
||||
};
|
||||
this.getDataList();
|
||||
},
|
||||
changeSort({ prop, order }) {
|
||||
if (!order) {
|
||||
this.searchForm.sort = "create_time";
|
||||
this.searchForm.order = "desc";
|
||||
} else {
|
||||
this.searchForm.sort = prop;
|
||||
this.searchForm.order = order === "ascending" ? "asc" : "desc";
|
||||
}
|
||||
this.getDataList();
|
||||
},
|
||||
handleSelectionChange(selection) {
|
||||
this.selectedRows = selection;
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getGoodsSkuData(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
this.data = (res.result.records || []).map((item) => ({
|
||||
...item,
|
||||
virtualSalesInput: this.getVirtualSalesValue(item),
|
||||
saving: false,
|
||||
}));
|
||||
this.total = res.result.total || 0;
|
||||
this.selectedRows = [];
|
||||
} else {
|
||||
this.data = [];
|
||||
this.total = 0;
|
||||
this.selectedRows = [];
|
||||
ElMessage.error((res && res.message) || "加载规格列表失败");
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.data = [];
|
||||
this.total = 0;
|
||||
this.selectedRows = [];
|
||||
ElMessage.error("加载规格列表失败");
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
getVirtualSalesValue(row) {
|
||||
const field = VIRTUAL_SALES_FIELDS.find(
|
||||
(item) => row[item] !== undefined && row[item] !== null && row[item] !== ""
|
||||
);
|
||||
return field ? Number(row[field]) || 0 : 0;
|
||||
},
|
||||
openVirtualSalesModal(row, index) {
|
||||
this.modalMode = "single";
|
||||
this.currentSku = { ...row };
|
||||
this.currentIndex = index;
|
||||
this.editForm.virtualSales = this.getVirtualSalesValue(row);
|
||||
this.editModalVisible = true;
|
||||
},
|
||||
openBatchVirtualSalesModal() {
|
||||
if (!this.selectedRows.length) {
|
||||
ElMessage.warning("请先选择要设置的商品规格");
|
||||
return;
|
||||
}
|
||||
this.modalMode = "batch";
|
||||
this.currentSku = null;
|
||||
this.currentIndex = -1;
|
||||
this.editForm.virtualSales = 0;
|
||||
this.editModalVisible = true;
|
||||
},
|
||||
handleCancelVirtualSales() {
|
||||
if (this.modalSubmitting) return;
|
||||
this.resetVirtualSalesModal();
|
||||
},
|
||||
resetVirtualSalesModal() {
|
||||
this.editModalVisible = false;
|
||||
this.modalMode = "single";
|
||||
this.currentSku = null;
|
||||
this.currentIndex = -1;
|
||||
this.editForm.virtualSales = 0;
|
||||
},
|
||||
handleSubmitVirtualSales() {
|
||||
const virtualSales = Number(this.editForm.virtualSales);
|
||||
if (!Number.isInteger(virtualSales) || virtualSales < 0) {
|
||||
ElMessage.warning("请输入大于等于 0 的整数虚拟销量");
|
||||
return;
|
||||
}
|
||||
if (this.modalMode === "batch") {
|
||||
this.handleBatchSubmitVirtualSales(virtualSales);
|
||||
return;
|
||||
}
|
||||
if (!this.currentSku || this.currentIndex < 0) return;
|
||||
defineOptions({ name: "goodsVirtualSales" });
|
||||
|
||||
const currentIndex = this.currentIndex;
|
||||
const currentSkuId = this.currentSku.id;
|
||||
this.modalSubmitting = true;
|
||||
if (this.data[currentIndex]) {
|
||||
this.data[currentIndex].saving = true;
|
||||
// 模板 ref(避免与同名数据字段冲突而改名)
|
||||
const searchFormRef = ref(null);
|
||||
const tableRef = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const editModalVisible = ref(false);
|
||||
const modalSubmitting = ref(false);
|
||||
const modalMode = ref("single");
|
||||
const currentSku = ref(null);
|
||||
const currentIndex = ref(-1);
|
||||
const selectedRows = ref([]);
|
||||
const editForm = reactive({
|
||||
virtualSales: 0,
|
||||
});
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
goodsName: "",
|
||||
goodsId: "",
|
||||
sn: "",
|
||||
storeName: "",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.value.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.value.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleReset() {
|
||||
searchForm.value = {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
goodsName: "",
|
||||
goodsId: "",
|
||||
sn: "",
|
||||
storeName: "",
|
||||
};
|
||||
getDataList();
|
||||
}
|
||||
function changeSort({ prop, order }) {
|
||||
if (!order) {
|
||||
searchForm.value.sort = "create_time";
|
||||
searchForm.value.order = "desc";
|
||||
} else {
|
||||
searchForm.value.sort = prop;
|
||||
searchForm.value.order = order === "ascending" ? "asc" : "desc";
|
||||
}
|
||||
getDataList();
|
||||
}
|
||||
function handleSelectionChange(selection) {
|
||||
selectedRows.value = selection;
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getGoodsSkuData(searchForm.value)
|
||||
.then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
data.value = (res.result.records || []).map((item) => ({
|
||||
...item,
|
||||
virtualSalesInput: getVirtualSalesValue(item),
|
||||
saving: false,
|
||||
}));
|
||||
total.value = res.result.total || 0;
|
||||
selectedRows.value = [];
|
||||
} else {
|
||||
data.value = [];
|
||||
total.value = 0;
|
||||
selectedRows.value = [];
|
||||
ElMessage.error((res && res.message) || "加载规格列表失败");
|
||||
}
|
||||
updateGoodsSkuVirtualSales(currentSkuId, { virtualSales })
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
if (this.data[currentIndex]) {
|
||||
this.data[currentIndex].virtualSales = virtualSales;
|
||||
this.data[currentIndex].virtualSalesInput = virtualSales;
|
||||
}
|
||||
ElMessage.success("虚拟销量设置成功");
|
||||
this.resetVirtualSalesModal();
|
||||
} else {
|
||||
ElMessage.error((res && res.message) || "规格虚拟销量设置失败");
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("规格虚拟销量设置失败");
|
||||
})
|
||||
.finally(() => {
|
||||
if (this.data[currentIndex]) {
|
||||
this.data[currentIndex].saving = false;
|
||||
}
|
||||
this.modalSubmitting = false;
|
||||
});
|
||||
},
|
||||
handleBatchSubmitVirtualSales(virtualSales) {
|
||||
const skuIds = this.selectedRows.map((item) => item.id).filter(Boolean);
|
||||
if (!skuIds.length) {
|
||||
ElMessage.warning("请先选择要设置的商品规格");
|
||||
return;
|
||||
})
|
||||
.catch(() => {
|
||||
data.value = [];
|
||||
total.value = 0;
|
||||
selectedRows.value = [];
|
||||
ElMessage.error("加载规格列表失败");
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function getVirtualSalesValue(row) {
|
||||
const field = VIRTUAL_SALES_FIELDS.find(
|
||||
(item) => row[item] !== undefined && row[item] !== null && row[item] !== ""
|
||||
);
|
||||
return field ? Number(row[field]) || 0 : 0;
|
||||
}
|
||||
function openVirtualSalesModal(row, index) {
|
||||
modalMode.value = "single";
|
||||
currentSku.value = { ...row };
|
||||
currentIndex.value = index;
|
||||
editForm.virtualSales = getVirtualSalesValue(row);
|
||||
editModalVisible.value = true;
|
||||
}
|
||||
function openBatchVirtualSalesModal() {
|
||||
if (!selectedRows.value.length) {
|
||||
ElMessage.warning("请先选择要设置的商品规格");
|
||||
return;
|
||||
}
|
||||
modalMode.value = "batch";
|
||||
currentSku.value = null;
|
||||
currentIndex.value = -1;
|
||||
editForm.virtualSales = 0;
|
||||
editModalVisible.value = true;
|
||||
}
|
||||
function handleCancelVirtualSales() {
|
||||
if (modalSubmitting.value) return;
|
||||
resetVirtualSalesModal();
|
||||
}
|
||||
function resetVirtualSalesModal() {
|
||||
editModalVisible.value = false;
|
||||
modalMode.value = "single";
|
||||
currentSku.value = null;
|
||||
currentIndex.value = -1;
|
||||
editForm.virtualSales = 0;
|
||||
}
|
||||
function handleSubmitVirtualSales() {
|
||||
const virtualSales = Number(editForm.virtualSales);
|
||||
if (!Number.isInteger(virtualSales) || virtualSales < 0) {
|
||||
ElMessage.warning("请输入大于等于 0 的整数虚拟销量");
|
||||
return;
|
||||
}
|
||||
if (modalMode.value === "batch") {
|
||||
handleBatchSubmitVirtualSales(virtualSales);
|
||||
return;
|
||||
}
|
||||
if (!currentSku.value || currentIndex.value < 0) return;
|
||||
|
||||
const curIndex = currentIndex.value;
|
||||
const currentSkuId = currentSku.value.id;
|
||||
modalSubmitting.value = true;
|
||||
if (data.value[curIndex]) {
|
||||
data.value[curIndex].saving = true;
|
||||
}
|
||||
updateGoodsSkuVirtualSales(currentSkuId, { virtualSales })
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
if (data.value[curIndex]) {
|
||||
data.value[curIndex].virtualSales = virtualSales;
|
||||
data.value[curIndex].virtualSalesInput = virtualSales;
|
||||
}
|
||||
ElMessage.success("虚拟销量设置成功");
|
||||
resetVirtualSalesModal();
|
||||
} else {
|
||||
ElMessage.error((res && res.message) || "规格虚拟销量设置失败");
|
||||
}
|
||||
this.modalSubmitting = true;
|
||||
batchUpdateGoodsSkuVirtualSales({ skuIds, virtualSales })
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
this.data = this.data.map((item) => {
|
||||
if (!skuIds.includes(item.id)) return item;
|
||||
return {
|
||||
...item,
|
||||
virtualSales,
|
||||
virtualSalesInput: virtualSales,
|
||||
};
|
||||
});
|
||||
this.selectedRows = [];
|
||||
this.$refs.table?.clearSelection();
|
||||
ElMessage.success("虚拟销量设置成功");
|
||||
this.resetVirtualSalesModal();
|
||||
} else {
|
||||
ElMessage.error((res && res.message) || "虚拟销量设置失败");
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("虚拟销量设置失败");
|
||||
})
|
||||
.finally(() => {
|
||||
this.modalSubmitting = false;
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("规格虚拟销量设置失败");
|
||||
})
|
||||
.finally(() => {
|
||||
if (data.value[curIndex]) {
|
||||
data.value[curIndex].saving = false;
|
||||
}
|
||||
modalSubmitting.value = false;
|
||||
});
|
||||
}
|
||||
function handleBatchSubmitVirtualSales(virtualSales) {
|
||||
const skuIds = selectedRows.value.map((item) => item.id).filter(Boolean);
|
||||
if (!skuIds.length) {
|
||||
ElMessage.warning("请先选择要设置的商品规格");
|
||||
return;
|
||||
}
|
||||
modalSubmitting.value = true;
|
||||
batchUpdateGoodsSkuVirtualSales({ skuIds, virtualSales })
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
data.value = data.value.map((item) => {
|
||||
if (!skuIds.includes(item.id)) return item;
|
||||
return {
|
||||
...item,
|
||||
virtualSales,
|
||||
virtualSalesInput: virtualSales,
|
||||
};
|
||||
});
|
||||
},
|
||||
getTotalSales(row) {
|
||||
return (Number(row.buyCount) || 0) + (Number(row.virtualSalesInput) || 0);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
selectedRows.value = [];
|
||||
tableRef.value?.clearSelection();
|
||||
ElMessage.success("虚拟销量设置成功");
|
||||
resetVirtualSalesModal();
|
||||
} else {
|
||||
ElMessage.error((res && res.message) || "虚拟销量设置失败");
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("虚拟销量设置失败");
|
||||
})
|
||||
.finally(() => {
|
||||
modalSubmitting.value = false;
|
||||
});
|
||||
}
|
||||
function getTotalSales(row) {
|
||||
return (Number(row.buyCount) || 0) + (Number(row.virtualSalesInput) || 0);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -48,37 +48,37 @@
|
||||
<h4>今日待办</h4>
|
||||
<div class="todo-list flex">
|
||||
<div class="todo-item" @click="navigateTo('applyGoods')">
|
||||
<div class="counts">{{ $store.state.notices.goods || 0 }}</div>
|
||||
<div class="counts">{{ notices.goods || 0 }}</div>
|
||||
<div>待审核商品</div>
|
||||
</div>
|
||||
<div class="todo-item" @click="navigateTo('shopAuth')">
|
||||
<div class="counts">{{ $store.state.notices.store || 0 }}</div>
|
||||
<div class="counts">{{ notices.store || 0 }}</div>
|
||||
<div>待审核店铺</div>
|
||||
</div>
|
||||
<div class="todo-item" @click="navigateTo('orderComplaint')">
|
||||
<div class="counts">{{ $store.state.notices.complain || 0 }}</div>
|
||||
<div class="counts">{{ notices.complain || 0 }}</div>
|
||||
<div>待审核投诉</div>
|
||||
</div>
|
||||
<div class="todo-item" @click="navigateTo('afterSaleOrder')">
|
||||
<div class="counts">{{ $store.state.notices.refund || 0 }}</div>
|
||||
<div class="counts">{{ notices.refund || 0 }}</div>
|
||||
<div>待审核售后</div>
|
||||
</div>
|
||||
<div class="todo-item" @click="goDistributionCash">
|
||||
<div class="counts">
|
||||
{{ $store.state.notices.distributionCash || 0 }}
|
||||
{{ notices.distributionCash || 0 }}
|
||||
</div>
|
||||
<div>待审核分销提现</div>
|
||||
</div>
|
||||
<div class="todo-item" @click="navigateTo('accountStatementBill')">
|
||||
<div class="counts">{{ $store.state.notices.waitPayBill || 0 }}</div>
|
||||
<div class="counts">{{ notices.waitPayBill || 0 }}</div>
|
||||
<div>待审核分账</div>
|
||||
</div>
|
||||
<div class="todo-item" @click="goAppointmentWorkOrders">
|
||||
<div class="counts">{{ $store.state.notices.appointmentWorkOrder || 0 }}</div>
|
||||
<div class="counts">{{ notices.appointmentWorkOrder || 0 }}</div>
|
||||
<div>未完成预约工单</div>
|
||||
</div>
|
||||
<div class="todo-item" @click="goProcurementAudit">
|
||||
<div class="counts">{{ $store.state.notices.procurementAudit || 0 }}</div>
|
||||
<div class="counts">{{ notices.procurementAudit || 0 }}</div>
|
||||
<div>待审核采购单</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -237,353 +237,372 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { Picture, User, List, DataLine } from "@element-plus/icons-vue";
|
||||
import { homeStatistics, hotGoods, hotShops, getNoticePage } from "@/api/index";
|
||||
import * as API_Goods from "@/api/goods";
|
||||
import { Chart } from "@antv/g2";
|
||||
import * as API_Member from "@/api/member";
|
||||
export default {
|
||||
name: "home",
|
||||
components: { Picture, User, List, DataLine },
|
||||
import { useAppStore } from "@/stores/app";
|
||||
|
||||
data() {
|
||||
return {
|
||||
topHotGoodsData: [], //热卖商品集合
|
||||
topHotShopsData: [], //热卖店铺集合
|
||||
awaitTodoData: "", //今日待办集合
|
||||
homeData: "", // 首页数据
|
||||
pvChart: "", // 流量统计
|
||||
orderChart: "", // 订单统计
|
||||
historyMemberChart: "", // 最近会员流量统计
|
||||
rangeKey: "LAST_SEVEN",
|
||||
rangeMonth: "",
|
||||
rangeOptions: [
|
||||
{ title: "今天", searchType: "TODAY" },
|
||||
{ title: "昨天", searchType: "YESTERDAY" },
|
||||
{ title: "过去7天", searchType: "LAST_SEVEN" },
|
||||
{ title: "过去30天", searchType: "LAST_THIRTY" },
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
recentMonths() {
|
||||
const list = [];
|
||||
const now = new Date();
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
list.push({
|
||||
value: `${year}-${month}`,
|
||||
label: `${year}年${month}月`,
|
||||
defineOptions({ name: "home" });
|
||||
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const topHotGoodsData = ref([]); //热卖商品集合
|
||||
const topHotShopsData = ref([]); //热卖店铺集合
|
||||
const awaitTodoData = ref(""); //今日待办集合
|
||||
const homeData = ref(""); // 首页数据
|
||||
const pvChart = ref(""); // 流量统计
|
||||
const orderChart = ref(""); // 订单统计
|
||||
const historyMemberChart = ref(""); // 最近会员流量统计
|
||||
const rangeKey = ref("LAST_SEVEN");
|
||||
const rangeMonth = ref("");
|
||||
const rangeOptions = ref([
|
||||
{ title: "今天", searchType: "TODAY" },
|
||||
{ title: "昨天", searchType: "YESTERDAY" },
|
||||
{ title: "过去7天", searchType: "LAST_SEVEN" },
|
||||
{ title: "过去30天", searchType: "LAST_THIRTY" },
|
||||
]);
|
||||
|
||||
// 原代码未在 data 声明、直接挂 this 的字段,补声明为普通变量
|
||||
let orderChartData = []; // 订单图表数据
|
||||
let data = []; // 浏览量图表数据
|
||||
let chartList = []; // 历史在线人数图表数据
|
||||
|
||||
const notices = computed(() => appStore.notices || {});
|
||||
|
||||
const recentMonths = computed(() => {
|
||||
const list = [];
|
||||
const now = new Date();
|
||||
for (let i = 0; i < 12; i += 1) {
|
||||
const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
list.push({
|
||||
value: `${year}-${month}`,
|
||||
label: `${year}年${month}月`,
|
||||
});
|
||||
}
|
||||
return list;
|
||||
});
|
||||
|
||||
const rangeTitle = computed(() => {
|
||||
if (rangeMonth.value) {
|
||||
const found = recentMonths.value.find((item) => item.value === rangeMonth.value);
|
||||
return found ? found.label : rangeMonth.value;
|
||||
}
|
||||
const found = rangeOptions.value.find((item) => item.searchType === rangeKey.value);
|
||||
return found ? found.title : "过去7天";
|
||||
});
|
||||
|
||||
// 路由跳转
|
||||
function navigateTo(name) {
|
||||
router.push({
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
// 跳转到分销提现,并打开待处理标签页
|
||||
function goDistributionCash() {
|
||||
router.push({
|
||||
name: 'distributionCash',
|
||||
query: { tabStatus: 'APPLY' }
|
||||
});
|
||||
}
|
||||
|
||||
function goAppointmentWorkOrders() {
|
||||
router.push({
|
||||
name: "manager-appointment-work-orders",
|
||||
query: { serviceStatus: "OPEN" },
|
||||
});
|
||||
}
|
||||
|
||||
function goProcurementAudit() {
|
||||
router.push({
|
||||
name: "manager-procurement-order-list",
|
||||
query: { status: "SUBMITTED" },
|
||||
});
|
||||
}
|
||||
|
||||
function rangeQuery() {
|
||||
if (rangeMonth.value) {
|
||||
const [year, month] = rangeMonth.value.split("-");
|
||||
return { year, month };
|
||||
}
|
||||
return { searchType: rangeKey.value || "LAST_SEVEN" };
|
||||
}
|
||||
|
||||
function changeRange(item) {
|
||||
rangeMonth.value = "";
|
||||
rangeKey.value = item.searchType;
|
||||
reloadRange();
|
||||
}
|
||||
|
||||
function changeMonth(value) {
|
||||
rangeMonth.value = value || "";
|
||||
if (!rangeMonth.value) {
|
||||
rangeKey.value = "LAST_SEVEN";
|
||||
}
|
||||
reloadRange();
|
||||
}
|
||||
|
||||
function reloadRange() {
|
||||
toHotGoods();
|
||||
topHotShops();
|
||||
getPvChart();
|
||||
initOrderChartList();
|
||||
}
|
||||
|
||||
// top10热卖商品
|
||||
async function toHotGoods() {
|
||||
let res = await hotGoods(rangeQuery());
|
||||
topHotGoodsData.value = res.success ? res.result || [] : [];
|
||||
}
|
||||
|
||||
// top10热卖店铺
|
||||
async function topHotShops() {
|
||||
let res = await hotShops(rangeQuery());
|
||||
topHotShopsData.value = res.success ? res.result || [] : [];
|
||||
}
|
||||
|
||||
// 今日待办
|
||||
async function awaitTodo() {
|
||||
let res = await getNoticePage();
|
||||
res.success ? (awaitTodoData.value = res.result) : "";
|
||||
}
|
||||
|
||||
//首页统计数据
|
||||
async function getHomeData() {
|
||||
let res = await homeStatistics();
|
||||
if (res.success) {
|
||||
const price = res.result.todayOrderPrice;
|
||||
const amount = price === null || price === undefined || price === "" || price === "null"
|
||||
? 0
|
||||
: Number(price);
|
||||
res.result.todayOrderPrice = Number.isNaN(amount) ? 0 : amount;
|
||||
homeData.value = res.result;
|
||||
}
|
||||
}
|
||||
|
||||
// 实例化订单图表
|
||||
async function initOrderChartList() {
|
||||
orderChart.value ? orderChart.value.clear() : ''
|
||||
const res = await API_Goods.getOrderChart(rangeQuery());
|
||||
if (res.success) {
|
||||
orderChartData = res.result || [];
|
||||
|
||||
if (!orderChart.value) {
|
||||
orderChart.value = new Chart({
|
||||
container: "orderChart",
|
||||
autoFit: true,
|
||||
height: 500,
|
||||
padding: [70, 70, 70, 70],
|
||||
});
|
||||
}
|
||||
|
||||
initOrderChart(); //订单表
|
||||
}
|
||||
}
|
||||
|
||||
// 订单表
|
||||
function initOrderChart() {
|
||||
let chartData = orderChartData || [];
|
||||
|
||||
chartData.forEach((item) => {
|
||||
const time = item.createTime ? String(item.createTime) : "";
|
||||
item.createTime = time.includes(" ") ? time.split(" ")[0] : time;
|
||||
item.title = "交易额";
|
||||
});
|
||||
orderChart.value.data(chartData);
|
||||
|
||||
orderChart.value.tooltip({
|
||||
showCrosshairs: true,
|
||||
shared: true,
|
||||
});
|
||||
|
||||
orderChart.value
|
||||
.line()
|
||||
.position("createTime*price")
|
||||
.label("price")
|
||||
.color("title")
|
||||
.shape("smooth");
|
||||
|
||||
orderChart.value
|
||||
.point()
|
||||
.position("createTime*price")
|
||||
.label("price")
|
||||
.color("title")
|
||||
.shape("circle")
|
||||
.style({
|
||||
stroke: "#fff",
|
||||
lineWidth: 1,
|
||||
});
|
||||
orderChart.value.area().position("createTime*price").color("title").shape("smooth");
|
||||
|
||||
orderChart.value.render();
|
||||
}
|
||||
|
||||
// 浏览量统计图
|
||||
function initPvChart() {
|
||||
let uv = [];
|
||||
let pv = [];
|
||||
const rows = Array.isArray(data) ? data : [];
|
||||
rows.forEach((item) => {
|
||||
uv.push({
|
||||
date: item.date,
|
||||
uvNum: item.uvNum,
|
||||
title: "访客数UV",
|
||||
pv: item.uvNum,
|
||||
});
|
||||
|
||||
pv.push({
|
||||
date: item.date,
|
||||
pvNum: item.pvNum,
|
||||
pv: item.pvNum,
|
||||
title: "浏览量PV",
|
||||
});
|
||||
});
|
||||
|
||||
let chartData = [...uv, ...pv];
|
||||
|
||||
pvChart.value.data(chartData);
|
||||
|
||||
pvChart.value.tooltip({
|
||||
showCrosshairs: true,
|
||||
shared: true,
|
||||
});
|
||||
|
||||
pvChart.value
|
||||
.line()
|
||||
.position("date*pv")
|
||||
.color("title")
|
||||
.label("pv")
|
||||
.shape("smooth");
|
||||
|
||||
pvChart.value
|
||||
.point()
|
||||
.position("date*pv")
|
||||
.color("title")
|
||||
.label("pv")
|
||||
.shape("circle")
|
||||
.style({
|
||||
stroke: "#fff",
|
||||
lineWidth: 1,
|
||||
});
|
||||
pvChart.value.area().position("date*pv").color("title").shape("smooth");
|
||||
|
||||
pvChart.value.render();
|
||||
}
|
||||
|
||||
// 浏览量
|
||||
async function getPvChart() {
|
||||
pvChart.value ? pvChart.value.clear() : ''
|
||||
API_Member.getStatisticsList(rangeQuery()).then((res) => {
|
||||
if (res.result) {
|
||||
data = res.result || [];
|
||||
|
||||
if (!pvChart.value) {
|
||||
pvChart.value = new Chart({
|
||||
container: "pvChart",
|
||||
autoFit: true,
|
||||
height: 500,
|
||||
padding: [70, 70, 70, 70],
|
||||
});
|
||||
}
|
||||
return list;
|
||||
},
|
||||
rangeTitle() {
|
||||
if (this.rangeMonth) {
|
||||
const found = this.recentMonths.find((item) => item.value === this.rangeMonth);
|
||||
return found ? found.label : this.rangeMonth;
|
||||
}
|
||||
const found = this.rangeOptions.find((item) => item.searchType === this.rangeKey);
|
||||
return found ? found.title : "过去7天";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 路由跳转
|
||||
navigateTo(name) {
|
||||
this.$router.push({
|
||||
name,
|
||||
initPvChart();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 实例化会员流量图表
|
||||
async function initHistoryMemberChartList() {
|
||||
historyMemberChart.value ? historyMemberChart.value.clear() : ''
|
||||
const res = await API_Member.historyMemberChartList();
|
||||
if (res.success) {
|
||||
chartList = res.result;
|
||||
|
||||
if (!historyMemberChart.value) {
|
||||
historyMemberChart.value = new Chart({
|
||||
container: "historyMemberChart",
|
||||
autoFit: true,
|
||||
height: 500,
|
||||
padding: [70, 70, 70, 70],
|
||||
});
|
||||
},
|
||||
// 跳转到分销提现,并打开待处理标签页
|
||||
goDistributionCash() {
|
||||
this.$router.push({
|
||||
name: 'distributionCash',
|
||||
query: { tabStatus: 'APPLY' }
|
||||
});
|
||||
},
|
||||
goAppointmentWorkOrders() {
|
||||
this.$router.push({
|
||||
name: "manager-appointment-work-orders",
|
||||
query: { serviceStatus: "OPEN" },
|
||||
});
|
||||
},
|
||||
goProcurementAudit() {
|
||||
this.$router.push({
|
||||
name: "manager-procurement-order-list",
|
||||
query: { status: "SUBMITTED" },
|
||||
});
|
||||
},
|
||||
rangeQuery() {
|
||||
if (this.rangeMonth) {
|
||||
const [year, month] = this.rangeMonth.split("-");
|
||||
return { year, month };
|
||||
}
|
||||
return { searchType: this.rangeKey || "LAST_SEVEN" };
|
||||
},
|
||||
changeRange(item) {
|
||||
this.rangeMonth = "";
|
||||
this.rangeKey = item.searchType;
|
||||
this.reloadRange();
|
||||
},
|
||||
changeMonth(value) {
|
||||
this.rangeMonth = value || "";
|
||||
if (!this.rangeMonth) {
|
||||
this.rangeKey = "LAST_SEVEN";
|
||||
}
|
||||
this.reloadRange();
|
||||
},
|
||||
reloadRange() {
|
||||
this.toHotGoods();
|
||||
this.topHotShops();
|
||||
this.getPvChart();
|
||||
this.initOrderChartList();
|
||||
},
|
||||
// top10热卖商品
|
||||
async toHotGoods() {
|
||||
let res = await hotGoods(this.rangeQuery());
|
||||
this.topHotGoodsData = res.success ? res.result || [] : [];
|
||||
},
|
||||
}
|
||||
|
||||
// top10热卖店铺
|
||||
async topHotShops() {
|
||||
let res = await hotShops(this.rangeQuery());
|
||||
this.topHotShopsData = res.success ? res.result || [] : [];
|
||||
},
|
||||
// 今日待办
|
||||
async awaitTodo() {
|
||||
let res = await getNoticePage();
|
||||
res.success ? (this.awaitTodoData = res.result) : "";
|
||||
},
|
||||
initHistoryMemberChart();
|
||||
}
|
||||
}
|
||||
|
||||
//首页统计数据
|
||||
async getHomeData() {
|
||||
let res = await homeStatistics();
|
||||
if (res.success) {
|
||||
const price = res.result.todayOrderPrice;
|
||||
const amount = price === null || price === undefined || price === "" || price === "null"
|
||||
? 0
|
||||
: Number(price);
|
||||
res.result.todayOrderPrice = Number.isNaN(amount) ? 0 : amount;
|
||||
this.homeData = res.result;
|
||||
}
|
||||
},
|
||||
// 历史在线人数
|
||||
function initHistoryMemberChart() {
|
||||
// 默认已经加载 legend-filter 交互
|
||||
let chartData = chartList;
|
||||
let num = [];
|
||||
let lastNum = [];
|
||||
chartData.forEach((item) => {
|
||||
num.push({
|
||||
date: item.date.substring(5),
|
||||
title: "最近48小时",
|
||||
num: item.num,
|
||||
res: item.num,
|
||||
});
|
||||
|
||||
// 实例化订单图表
|
||||
async initOrderChartList() {
|
||||
this.orderChart ? this.orderChart.clear() : ''
|
||||
const res = await API_Goods.getOrderChart(this.rangeQuery());
|
||||
if (res.success) {
|
||||
this.orderChartData = res.result || [];
|
||||
lastNum.push({
|
||||
date: item.date.substring(5),
|
||||
title: "上一周期",
|
||||
lastNum: item.lastNum || 0,
|
||||
res: item.lastNum || 0,
|
||||
});
|
||||
});
|
||||
let params = [...num, ...lastNum];
|
||||
historyMemberChart.value.data(params);
|
||||
|
||||
if (!this.orderChart) {
|
||||
this.orderChart = new Chart({
|
||||
container: "orderChart",
|
||||
autoFit: true,
|
||||
height: 500,
|
||||
padding: [70, 70, 70, 70],
|
||||
});
|
||||
}
|
||||
historyMemberChart.value.tooltip({
|
||||
showCrosshairs: true,
|
||||
shared: true,
|
||||
});
|
||||
|
||||
this.initOrderChart(); //订单表
|
||||
}
|
||||
},
|
||||
historyMemberChart.value
|
||||
.line()
|
||||
.position("date*res")
|
||||
.color("title")
|
||||
.label("res")
|
||||
.shape("smooth");
|
||||
historyMemberChart.value
|
||||
.point()
|
||||
.position("date*res")
|
||||
.color("title")
|
||||
.label("res")
|
||||
.shape("circle");
|
||||
|
||||
// 订单表
|
||||
initOrderChart() {
|
||||
let data = this.orderChartData || [];
|
||||
historyMemberChart.value
|
||||
.area()
|
||||
.position("date*res")
|
||||
.color("title")
|
||||
.shape("smooth");
|
||||
|
||||
data.forEach((item) => {
|
||||
const time = item.createTime ? String(item.createTime) : "";
|
||||
item.createTime = time.includes(" ") ? time.split(" ")[0] : time;
|
||||
item.title = "交易额";
|
||||
});
|
||||
this.orderChart.data(data);
|
||||
historyMemberChart.value.render();
|
||||
}
|
||||
|
||||
this.orderChart.tooltip({
|
||||
showCrosshairs: true,
|
||||
shared: true,
|
||||
});
|
||||
// 初始化信息
|
||||
function init() {
|
||||
toHotGoods();
|
||||
topHotShops();
|
||||
awaitTodo();
|
||||
getHomeData();
|
||||
getPvChart();
|
||||
initOrderChartList();
|
||||
initHistoryMemberChartList();
|
||||
}
|
||||
|
||||
this.orderChart
|
||||
.line()
|
||||
.position("createTime*price")
|
||||
.label("price")
|
||||
.color("title")
|
||||
.shape("smooth");
|
||||
|
||||
this.orderChart
|
||||
.point()
|
||||
.position("createTime*price")
|
||||
.label("price")
|
||||
.color("title")
|
||||
.shape("circle")
|
||||
.style({
|
||||
stroke: "#fff",
|
||||
lineWidth: 1,
|
||||
});
|
||||
this.orderChart.area().position("createTime*price").color("title").shape("smooth");
|
||||
|
||||
this.orderChart.render();
|
||||
},
|
||||
|
||||
// 浏览量统计图
|
||||
initPvChart() {
|
||||
let uv = [];
|
||||
let pv = [];
|
||||
const rows = Array.isArray(this.data) ? this.data : [];
|
||||
rows.forEach((item) => {
|
||||
uv.push({
|
||||
date: item.date,
|
||||
uvNum: item.uvNum,
|
||||
title: "访客数UV",
|
||||
pv: item.uvNum,
|
||||
});
|
||||
|
||||
pv.push({
|
||||
date: item.date,
|
||||
pvNum: item.pvNum,
|
||||
pv: item.pvNum,
|
||||
title: "浏览量PV",
|
||||
});
|
||||
});
|
||||
|
||||
let data = [...uv, ...pv];
|
||||
|
||||
this.pvChart.data(data);
|
||||
|
||||
this.pvChart.tooltip({
|
||||
showCrosshairs: true,
|
||||
shared: true,
|
||||
});
|
||||
|
||||
this.pvChart
|
||||
.line()
|
||||
.position("date*pv")
|
||||
.color("title")
|
||||
.label("pv")
|
||||
.shape("smooth");
|
||||
|
||||
this.pvChart
|
||||
.point()
|
||||
.position("date*pv")
|
||||
.color("title")
|
||||
.label("pv")
|
||||
.shape("circle")
|
||||
.style({
|
||||
stroke: "#fff",
|
||||
lineWidth: 1,
|
||||
});
|
||||
this.pvChart.area().position("date*pv").color("title").shape("smooth");
|
||||
|
||||
this.pvChart.render();
|
||||
},
|
||||
|
||||
// 浏览量
|
||||
async getPvChart() {
|
||||
this.pvChart ? this.pvChart.clear() : ''
|
||||
API_Member.getStatisticsList(this.rangeQuery()).then((res) => {
|
||||
if (res.result) {
|
||||
this.data = res.result || [];
|
||||
|
||||
if (!this.pvChart) {
|
||||
this.pvChart = new Chart({
|
||||
container: "pvChart",
|
||||
autoFit: true,
|
||||
height: 500,
|
||||
padding: [70, 70, 70, 70],
|
||||
});
|
||||
}
|
||||
this.initPvChart();
|
||||
}
|
||||
});
|
||||
},
|
||||
// 实例化会员流量图表
|
||||
async initHistoryMemberChartList() {
|
||||
this.historyMemberChart ? this.historyMemberChart.clear() : ''
|
||||
const res = await API_Member.historyMemberChartList();
|
||||
if (res.success) {
|
||||
this.chartList = res.result;
|
||||
|
||||
if (!this.historyMemberChart) {
|
||||
this.historyMemberChart = new Chart({
|
||||
container: "historyMemberChart",
|
||||
autoFit: true,
|
||||
height: 500,
|
||||
padding: [70, 70, 70, 70],
|
||||
});
|
||||
}
|
||||
|
||||
this.initHistoryMemberChart();
|
||||
}
|
||||
},
|
||||
// 历史在线人数
|
||||
initHistoryMemberChart() {
|
||||
// 默认已经加载 legend-filter 交互
|
||||
let data = this.chartList;
|
||||
let num = [];
|
||||
let lastNum = [];
|
||||
data.forEach((item) => {
|
||||
num.push({
|
||||
date: item.date.substring(5),
|
||||
title: "最近48小时",
|
||||
num: item.num,
|
||||
res: item.num,
|
||||
});
|
||||
|
||||
lastNum.push({
|
||||
date: item.date.substring(5),
|
||||
title: "上一周期",
|
||||
lastNum: item.lastNum || 0,
|
||||
res: item.lastNum || 0,
|
||||
});
|
||||
});
|
||||
let params = [...num, ...lastNum];
|
||||
this.historyMemberChart.data(params);
|
||||
|
||||
this.historyMemberChart.tooltip({
|
||||
showCrosshairs: true,
|
||||
shared: true,
|
||||
});
|
||||
|
||||
this.historyMemberChart
|
||||
.line()
|
||||
.position("date*res")
|
||||
.color("title")
|
||||
.label("res")
|
||||
.shape("smooth");
|
||||
this.historyMemberChart
|
||||
.point()
|
||||
.position("date*res")
|
||||
.color("title")
|
||||
.label("res")
|
||||
.shape("circle");
|
||||
|
||||
this.historyMemberChart
|
||||
.area()
|
||||
.position("date*res")
|
||||
.color("title")
|
||||
.shape("smooth");
|
||||
|
||||
this.historyMemberChart.render();
|
||||
},
|
||||
// 初始化信息
|
||||
init() {
|
||||
this.toHotGoods();
|
||||
this.topHotShops();
|
||||
this.awaitTodo();
|
||||
this.getHomeData();
|
||||
this.getPvChart();
|
||||
this.initOrderChartList();
|
||||
this.initHistoryMemberChartList();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./home.scss";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<template #header>
|
||||
<span class="page-title">创建直播</span>
|
||||
</template>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px" class="live-form">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px" class="live-form">
|
||||
<el-form-item label="直播标题" prop="title">
|
||||
<el-input
|
||||
v-model="form.title"
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
@callback="callbackSelected"
|
||||
@@ -77,11 +77,16 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { nextTick, reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { Picture } from "@element-plus/icons-vue";
|
||||
import { createLive } from "@/api/live";
|
||||
import { Message } from "@/utils/message";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
|
||||
defineOptions({ name: "live-add" });
|
||||
|
||||
function getCharLength(str) {
|
||||
if (!str) return 0;
|
||||
let len = 0;
|
||||
@@ -98,88 +103,85 @@ function buildEndTime(startTime, hours = 24) {
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "live-add",
|
||||
components: { Picture, ossManage },
|
||||
data() {
|
||||
const validateTitle = (rule, value, callback) => {
|
||||
const len = getCharLength(value);
|
||||
if (len < 6) {
|
||||
callback(new Error("直播间名字最短3个汉字"));
|
||||
return;
|
||||
}
|
||||
if (len > 34) {
|
||||
callback(new Error("直播间名字最长17个汉字"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
return {
|
||||
picModelFlag: false,
|
||||
submitting: false,
|
||||
form: {
|
||||
title: "",
|
||||
displayMode: "0",
|
||||
startTime: "",
|
||||
coverImg: "",
|
||||
liveIntroduce: "",
|
||||
},
|
||||
rules: {
|
||||
title: [
|
||||
{ required: true, message: "请输入直播标题", trigger: "blur" },
|
||||
{ validator: validateTitle, trigger: "blur" },
|
||||
],
|
||||
displayMode: [{ required: true, message: "请选择显示模式", trigger: "change" }],
|
||||
startTime: [{ required: true, message: "请选择直播开始时间", trigger: "change" }],
|
||||
coverImg: [{ required: true, message: "请上传直播封面", trigger: "change" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
openCoverPicker() {
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (val?.url) {
|
||||
this.form.coverImg = val.url;
|
||||
this.$refs.form?.validateField("coverImg");
|
||||
}
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitting = true;
|
||||
createLive({
|
||||
title: this.form.title,
|
||||
displayMode: this.form.displayMode,
|
||||
coverImg: this.form.coverImg,
|
||||
startTime: this.form.startTime,
|
||||
endTime: buildEndTime(this.form.startTime),
|
||||
liveIntroduce: this.form.liveIntroduce,
|
||||
})
|
||||
.then((res) => {
|
||||
this.submitting = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("创建成功");
|
||||
this.$router.push({ path: "/live-list" });
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.submitting = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
handleBack() {
|
||||
this.$router.push({ path: "/live-list" });
|
||||
},
|
||||
},
|
||||
const router = useRouter();
|
||||
|
||||
const formRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
const picModelFlag = ref(false);
|
||||
const submitting = ref(false);
|
||||
const form = reactive({
|
||||
title: "",
|
||||
displayMode: "0",
|
||||
startTime: "",
|
||||
coverImg: "",
|
||||
liveIntroduce: "",
|
||||
});
|
||||
|
||||
const validateTitle = (rule, value, callback) => {
|
||||
const len = getCharLength(value);
|
||||
if (len < 6) {
|
||||
callback(new Error("直播间名字最短3个汉字"));
|
||||
return;
|
||||
}
|
||||
if (len > 34) {
|
||||
callback(new Error("直播间名字最长17个汉字"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
const rules = {
|
||||
title: [
|
||||
{ required: true, message: "请输入直播标题", trigger: "blur" },
|
||||
{ validator: validateTitle, trigger: "blur" },
|
||||
],
|
||||
displayMode: [{ required: true, message: "请选择显示模式", trigger: "change" }],
|
||||
startTime: [{ required: true, message: "请选择直播开始时间", trigger: "change" }],
|
||||
coverImg: [{ required: true, message: "请上传直播封面", trigger: "change" }],
|
||||
};
|
||||
|
||||
function openCoverPicker() {
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
if (val?.url) {
|
||||
form.coverImg = val.url;
|
||||
formRef.value?.validateField("coverImg");
|
||||
}
|
||||
}
|
||||
function handleSubmit() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitting.value = true;
|
||||
createLive({
|
||||
title: form.title,
|
||||
displayMode: form.displayMode,
|
||||
coverImg: form.coverImg,
|
||||
startTime: form.startTime,
|
||||
endTime: buildEndTime(form.startTime),
|
||||
liveIntroduce: form.liveIntroduce,
|
||||
})
|
||||
.then((res) => {
|
||||
submitting.value = false;
|
||||
if (res.success) {
|
||||
Message.success("创建成功");
|
||||
router.push({ path: "/live-list" });
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
submitting.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
function handleBack() {
|
||||
router.push({ path: "/live-list" });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -193,7 +193,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted } from "vue";
|
||||
import { Refresh } from "@element-plus/icons-vue";
|
||||
import {
|
||||
authLiveMessage,
|
||||
@@ -204,243 +205,261 @@ import {
|
||||
queryLiveMessagePage,
|
||||
unblockUser,
|
||||
} from "@/api/live";
|
||||
import { Message } from "@/utils/message";
|
||||
import { unixToDate } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "CommentReview",
|
||||
components: { Refresh },
|
||||
props: {
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
embedded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
defineOptions({ name: "CommentReview" });
|
||||
|
||||
const props = defineProps({
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeTab: "message",
|
||||
searchKeyword: "",
|
||||
messageLoading: false,
|
||||
userLoading: false,
|
||||
blockLoading: false,
|
||||
messageList: [],
|
||||
userList: [],
|
||||
blockList: [],
|
||||
messagePagination: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
},
|
||||
userPagination: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
},
|
||||
blockPagination: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
},
|
||||
blockDialogVisible: false,
|
||||
blockTarget: null,
|
||||
blockReason: "",
|
||||
blockSubmitting: false,
|
||||
};
|
||||
embedded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
watch: {
|
||||
liveId() {
|
||||
this.loadMessages();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadMessages();
|
||||
},
|
||||
methods: {
|
||||
formatTime(time) {
|
||||
if (!time) return "-";
|
||||
return this.$filters.unixToDate(time);
|
||||
},
|
||||
authStatusText(status) {
|
||||
const map = { PASS: "已通过", REFUSE: "已拒绝", TOBEAUDITED: "待审核" };
|
||||
return map[status] || status || "待审核";
|
||||
},
|
||||
authStatusTagType(status) {
|
||||
const map = { PASS: "success", REFUSE: "danger", TOBEAUDITED: "warning" };
|
||||
return map[status] || "info";
|
||||
},
|
||||
canPassAuth(row) {
|
||||
const status = row?.authStatus;
|
||||
return status === "TOBEAUDITED" || status === "REFUSE" || !status;
|
||||
},
|
||||
canRejectAuth(row) {
|
||||
const status = row?.authStatus;
|
||||
return status === "TOBEAUDITED" || status === "PASS" || !status;
|
||||
},
|
||||
parsePageResult(res) {
|
||||
if (!res) return { records: [], total: 0 };
|
||||
if (res.records) {
|
||||
return { records: res.records || [], total: res.total || 0 };
|
||||
});
|
||||
|
||||
const activeTab = ref("message");
|
||||
const searchKeyword = ref("");
|
||||
const messageLoading = ref(false);
|
||||
const userLoading = ref(false);
|
||||
const blockLoading = ref(false);
|
||||
const messageList = ref([]);
|
||||
const userList = ref([]);
|
||||
const blockList = ref([]);
|
||||
const messagePagination = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const userPagination = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const blockPagination = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const blockDialogVisible = ref(false);
|
||||
const blockTarget = ref(null);
|
||||
const blockReason = ref("");
|
||||
const blockSubmitting = ref(false);
|
||||
|
||||
watch(
|
||||
() => props.liveId,
|
||||
() => {
|
||||
loadMessages();
|
||||
}
|
||||
);
|
||||
|
||||
function formatTime(time) {
|
||||
if (!time) return "-";
|
||||
return unixToDate(time);
|
||||
}
|
||||
|
||||
function authStatusText(status) {
|
||||
const map = { PASS: "已通过", REFUSE: "已拒绝", TOBEAUDITED: "待审核" };
|
||||
return map[status] || status || "待审核";
|
||||
}
|
||||
|
||||
function authStatusTagType(status) {
|
||||
const map = { PASS: "success", REFUSE: "danger", TOBEAUDITED: "warning" };
|
||||
return map[status] || "info";
|
||||
}
|
||||
|
||||
function canPassAuth(row) {
|
||||
const status = row?.authStatus;
|
||||
return status === "TOBEAUDITED" || status === "REFUSE" || !status;
|
||||
}
|
||||
|
||||
function canRejectAuth(row) {
|
||||
const status = row?.authStatus;
|
||||
return status === "TOBEAUDITED" || status === "PASS" || !status;
|
||||
}
|
||||
|
||||
function parsePageResult(res) {
|
||||
if (!res) return { records: [], total: 0 };
|
||||
if (res.records) {
|
||||
return { records: res.records || [], total: res.total || 0 };
|
||||
}
|
||||
if (res.result?.records) {
|
||||
return { records: res.result.records || [], total: res.result.total || 0 };
|
||||
}
|
||||
if (res.success && res.result) {
|
||||
return { records: res.result.records || [], total: res.result.total || 0 };
|
||||
}
|
||||
return { records: [], total: 0 };
|
||||
}
|
||||
|
||||
function onTabChange(tab) {
|
||||
if (tab === "message") loadMessages();
|
||||
if (tab === "user") loadUsers();
|
||||
if (tab === "block") loadBlocks();
|
||||
}
|
||||
|
||||
function loadMessages() {
|
||||
if (!props.liveId) return;
|
||||
messageLoading.value = true;
|
||||
const params = {
|
||||
liveStreamId: props.liveId,
|
||||
pageNumber: messagePagination.pageNumber,
|
||||
pageSize: messagePagination.pageSize,
|
||||
};
|
||||
if (searchKeyword.value?.trim()) {
|
||||
params.keyword = searchKeyword.value.trim();
|
||||
}
|
||||
queryLiveMessagePage(params)
|
||||
.then((res) => {
|
||||
messageLoading.value = false;
|
||||
if (res.success) {
|
||||
const page = parsePageResult(res);
|
||||
messageList.value = page.records;
|
||||
messagePagination.total = page.total;
|
||||
}
|
||||
if (res.result?.records) {
|
||||
return { records: res.result.records || [], total: res.result.total || 0 };
|
||||
})
|
||||
.catch(() => {
|
||||
messageLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function loadUsers() {
|
||||
if (!props.liveId) return;
|
||||
userLoading.value = true;
|
||||
liveUserList({
|
||||
liveRoomId: props.liveId,
|
||||
pageNumber: userPagination.pageNumber,
|
||||
pageSize: userPagination.pageSize,
|
||||
})
|
||||
.then((res) => {
|
||||
userLoading.value = false;
|
||||
if (res.success) {
|
||||
userList.value = res.result?.records || [];
|
||||
userPagination.total = res.result?.total || 0;
|
||||
}
|
||||
if (res.success && res.result) {
|
||||
return { records: res.result.records || [], total: res.result.total || 0 };
|
||||
})
|
||||
.catch(() => {
|
||||
userLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function loadBlocks() {
|
||||
if (!props.liveId) return;
|
||||
blockLoading.value = true;
|
||||
getLiveBlockPage({
|
||||
liveRoomId: props.liveId,
|
||||
pageNumber: blockPagination.pageNumber,
|
||||
pageSize: blockPagination.pageSize,
|
||||
})
|
||||
.then((res) => {
|
||||
blockLoading.value = false;
|
||||
const page = parsePageResult(res);
|
||||
blockList.value = page.records;
|
||||
blockPagination.total = page.total;
|
||||
})
|
||||
.catch(() => {
|
||||
blockLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleAuth(record, authStatus) {
|
||||
authLiveMessage({
|
||||
...record,
|
||||
authStatus,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
loadMessages();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleToggleMute(record) {
|
||||
const muteFlag = !record.muteFlag;
|
||||
editLiveUserMute({
|
||||
liveUserid: record.id,
|
||||
muteFlag,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success(muteFlag ? "已禁言" : "已解除禁言");
|
||||
loadUsers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleBlock(record) {
|
||||
blockTarget.value = record;
|
||||
blockReason.value = "";
|
||||
blockDialogVisible.value = true;
|
||||
}
|
||||
|
||||
function confirmBlock() {
|
||||
if (!blockReason.value?.trim()) {
|
||||
Message.warning("请输入拉黑原因");
|
||||
return;
|
||||
}
|
||||
blockSubmitting.value = true;
|
||||
blockUser({
|
||||
liveRoomId: props.liveId,
|
||||
userId: blockTarget.value?.userId || blockTarget.value?.id,
|
||||
reason: blockReason.value.trim(),
|
||||
})
|
||||
.then((res) => {
|
||||
blockSubmitting.value = false;
|
||||
if (res.success) {
|
||||
Message.success("拉黑成功");
|
||||
blockDialogVisible.value = false;
|
||||
loadUsers();
|
||||
loadBlocks();
|
||||
}
|
||||
return { records: [], total: 0 };
|
||||
},
|
||||
onTabChange(tab) {
|
||||
if (tab === "message") this.loadMessages();
|
||||
if (tab === "user") this.loadUsers();
|
||||
if (tab === "block") this.loadBlocks();
|
||||
},
|
||||
loadMessages() {
|
||||
if (!this.liveId) return;
|
||||
this.messageLoading = true;
|
||||
const params = {
|
||||
liveStreamId: this.liveId,
|
||||
pageNumber: this.messagePagination.pageNumber,
|
||||
pageSize: this.messagePagination.pageSize,
|
||||
};
|
||||
if (this.searchKeyword?.trim()) {
|
||||
params.keyword = this.searchKeyword.trim();
|
||||
}
|
||||
queryLiveMessagePage(params)
|
||||
.then((res) => {
|
||||
this.messageLoading = false;
|
||||
if (res.success) {
|
||||
const page = this.parsePageResult(res);
|
||||
this.messageList = page.records;
|
||||
this.messagePagination.total = page.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.messageLoading = false;
|
||||
});
|
||||
},
|
||||
loadUsers() {
|
||||
if (!this.liveId) return;
|
||||
this.userLoading = true;
|
||||
liveUserList({
|
||||
liveRoomId: this.liveId,
|
||||
pageNumber: this.userPagination.pageNumber,
|
||||
pageSize: this.userPagination.pageSize,
|
||||
})
|
||||
.then((res) => {
|
||||
this.userLoading = false;
|
||||
if (res.success) {
|
||||
this.userList = res.result?.records || [];
|
||||
this.userPagination.total = res.result?.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.userLoading = false;
|
||||
});
|
||||
},
|
||||
loadBlocks() {
|
||||
if (!this.liveId) return;
|
||||
this.blockLoading = true;
|
||||
getLiveBlockPage({
|
||||
liveRoomId: this.liveId,
|
||||
pageNumber: this.blockPagination.pageNumber,
|
||||
pageSize: this.blockPagination.pageSize,
|
||||
})
|
||||
.then((res) => {
|
||||
this.blockLoading = false;
|
||||
const page = this.parsePageResult(res);
|
||||
this.blockList = page.records;
|
||||
this.blockPagination.total = page.total;
|
||||
})
|
||||
.catch(() => {
|
||||
this.blockLoading = false;
|
||||
});
|
||||
},
|
||||
handleAuth(record, authStatus) {
|
||||
authLiveMessage({
|
||||
...record,
|
||||
authStatus,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.loadMessages();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleToggleMute(record) {
|
||||
const muteFlag = !record.muteFlag;
|
||||
editLiveUserMute({
|
||||
liveUserid: record.id,
|
||||
muteFlag,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success(muteFlag ? "已禁言" : "已解除禁言");
|
||||
this.loadUsers();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleBlock(record) {
|
||||
this.blockTarget = record;
|
||||
this.blockReason = "";
|
||||
this.blockDialogVisible = true;
|
||||
},
|
||||
confirmBlock() {
|
||||
if (!this.blockReason?.trim()) {
|
||||
this.$Message.warning("请输入拉黑原因");
|
||||
return;
|
||||
}
|
||||
this.blockSubmitting = true;
|
||||
blockUser({
|
||||
liveRoomId: this.liveId,
|
||||
userId: this.blockTarget?.userId || this.blockTarget?.id,
|
||||
reason: this.blockReason.trim(),
|
||||
})
|
||||
.then((res) => {
|
||||
this.blockSubmitting = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("拉黑成功");
|
||||
this.blockDialogVisible = false;
|
||||
this.loadUsers();
|
||||
this.loadBlocks();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.blockSubmitting = false;
|
||||
});
|
||||
},
|
||||
handleUnblock(record) {
|
||||
unblockUser({
|
||||
liveRoomId: this.liveId,
|
||||
userId: record.userId || record.id,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("已解除拉黑");
|
||||
this.loadBlocks();
|
||||
}
|
||||
});
|
||||
},
|
||||
onMessagePageSizeChange() {
|
||||
this.messagePagination.pageNumber = 1;
|
||||
this.loadMessages();
|
||||
},
|
||||
onUserPageSizeChange() {
|
||||
this.userPagination.pageNumber = 1;
|
||||
this.loadUsers();
|
||||
},
|
||||
onBlockPageSizeChange() {
|
||||
this.blockPagination.pageNumber = 1;
|
||||
this.loadBlocks();
|
||||
},
|
||||
handleSearch() {
|
||||
this.messagePagination.pageNumber = 1;
|
||||
this.loadMessages();
|
||||
},
|
||||
handleRefresh() {
|
||||
this.loadMessages();
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
blockSubmitting.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleUnblock(record) {
|
||||
unblockUser({
|
||||
liveRoomId: props.liveId,
|
||||
userId: record.userId || record.id,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("已解除拉黑");
|
||||
loadBlocks();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function onMessagePageSizeChange() {
|
||||
messagePagination.pageNumber = 1;
|
||||
loadMessages();
|
||||
}
|
||||
|
||||
function onUserPageSizeChange() {
|
||||
userPagination.pageNumber = 1;
|
||||
loadUsers();
|
||||
}
|
||||
|
||||
function onBlockPageSizeChange() {
|
||||
blockPagination.pageNumber = 1;
|
||||
loadBlocks();
|
||||
}
|
||||
|
||||
function handleSearch() {
|
||||
messagePagination.pageNumber = 1;
|
||||
loadMessages();
|
||||
}
|
||||
|
||||
function handleRefresh() {
|
||||
loadMessages();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadMessages();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -105,7 +105,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, watch } from "vue";
|
||||
import {
|
||||
cancelRecommendLiveCoupon,
|
||||
getLiveCouponList,
|
||||
@@ -114,189 +115,192 @@ import {
|
||||
setRecommendLiveCoupon,
|
||||
} from "@/api/live";
|
||||
import { getPlatformCouponList } from "@/api/promotion";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
import { unitPrice } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "LiveCoupon",
|
||||
props: {
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
liveRoomName: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
defineOptions({ name: "LiveCoupon" });
|
||||
|
||||
const props = defineProps({
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
couponList: [],
|
||||
selectedIds: [],
|
||||
pickerVisible: false,
|
||||
pickerLoading: false,
|
||||
pickerSubmitting: false,
|
||||
pickerList: [],
|
||||
pickerTotal: 0,
|
||||
pickerSelected: [],
|
||||
pickerSearch: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
couponName: "",
|
||||
promotionStatus: "START",
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
},
|
||||
};
|
||||
liveRoomName: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
watch: {
|
||||
liveId() {
|
||||
this.loadCoupons();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadCoupons();
|
||||
},
|
||||
methods: {
|
||||
formatPrice(price) {
|
||||
return this.$filters.unitPrice(price, "¥");
|
||||
},
|
||||
promotionStatusText(status) {
|
||||
const map = { NEW: "未开始", START: "已开始", END: "已结束", CLOSE: "已关闭" };
|
||||
return map[status] || "未知";
|
||||
},
|
||||
promotionStatusTagType(status) {
|
||||
const map = { NEW: "info", START: "success", END: "warning", CLOSE: "danger" };
|
||||
return map[status] || "info";
|
||||
},
|
||||
loadCoupons() {
|
||||
if (!this.liveId) return;
|
||||
this.loading = true;
|
||||
getLiveCouponList(this.liveId)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.couponList = res.result || [];
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleSelectionChange(rows) {
|
||||
this.selectedIds = (rows || []).map((r) => r.id);
|
||||
},
|
||||
openCouponSelector() {
|
||||
this.pickerVisible = true;
|
||||
this.pickerSelected = [];
|
||||
this.pickerSearch.pageNumber = 1;
|
||||
this.loadPickerCoupons();
|
||||
},
|
||||
loadPickerCoupons() {
|
||||
this.pickerLoading = true;
|
||||
getPlatformCouponList(this.pickerSearch)
|
||||
.then((res) => {
|
||||
this.pickerLoading = false;
|
||||
if (res.success) {
|
||||
this.pickerList = res.result?.records || [];
|
||||
this.pickerTotal = res.result?.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.pickerLoading = false;
|
||||
});
|
||||
},
|
||||
onPickerPageSizeChange() {
|
||||
this.pickerSearch.pageNumber = 1;
|
||||
this.loadPickerCoupons();
|
||||
},
|
||||
handlePickerSelectionChange(rows) {
|
||||
this.pickerSelected = rows || [];
|
||||
},
|
||||
buildLiveCouponPayload(item) {
|
||||
return {
|
||||
couponId: item.id,
|
||||
couponPrice: Number(item.price ?? 0),
|
||||
couponName: item.couponName || "",
|
||||
liveRoomId: this.liveId,
|
||||
liveRoomName: this.liveRoomName || "",
|
||||
hideFlag: false,
|
||||
recommend: false,
|
||||
};
|
||||
},
|
||||
confirmPicker() {
|
||||
if (!this.pickerSelected.length) {
|
||||
this.$Message.warning("请选择优惠券");
|
||||
return;
|
||||
});
|
||||
|
||||
const table = ref(null);
|
||||
const pickerTable = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const couponList = ref([]);
|
||||
const selectedIds = ref([]);
|
||||
const pickerVisible = ref(false);
|
||||
const pickerLoading = ref(false);
|
||||
const pickerSubmitting = ref(false);
|
||||
const pickerList = ref([]);
|
||||
const pickerTotal = ref(0);
|
||||
const pickerSelected = ref([]);
|
||||
const pickerSearch = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
couponName: "",
|
||||
promotionStatus: "START",
|
||||
sort: "create_time",
|
||||
order: "desc",
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.liveId,
|
||||
() => {
|
||||
loadCoupons();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
loadCoupons();
|
||||
});
|
||||
|
||||
function formatPrice(price) {
|
||||
return unitPrice(price, "¥");
|
||||
}
|
||||
function promotionStatusText(status) {
|
||||
const map = { NEW: "未开始", START: "已开始", END: "已结束", CLOSE: "已关闭" };
|
||||
return map[status] || "未知";
|
||||
}
|
||||
function promotionStatusTagType(status) {
|
||||
const map = { NEW: "info", START: "success", END: "warning", CLOSE: "danger" };
|
||||
return map[status] || "info";
|
||||
}
|
||||
function loadCoupons() {
|
||||
if (!props.liveId) return;
|
||||
loading.value = true;
|
||||
getLiveCouponList(props.liveId)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
couponList.value = res.result || [];
|
||||
}
|
||||
const existingCouponIds = new Set(this.couponList.map((c) => String(c.couponId)));
|
||||
const payload = this.pickerSelected
|
||||
.filter((item) => !existingCouponIds.has(String(item.id)))
|
||||
.map((item) => this.buildLiveCouponPayload(item));
|
||||
if (!payload.length) {
|
||||
this.$Message.warning("所选优惠券均已添加");
|
||||
return;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function handleSelectionChange(rows) {
|
||||
selectedIds.value = (rows || []).map((r) => r.id);
|
||||
}
|
||||
function openCouponSelector() {
|
||||
pickerVisible.value = true;
|
||||
pickerSelected.value = [];
|
||||
pickerSearch.pageNumber = 1;
|
||||
loadPickerCoupons();
|
||||
}
|
||||
function loadPickerCoupons() {
|
||||
pickerLoading.value = true;
|
||||
getPlatformCouponList(pickerSearch)
|
||||
.then((res) => {
|
||||
pickerLoading.value = false;
|
||||
if (res.success) {
|
||||
pickerList.value = res.result?.records || [];
|
||||
pickerTotal.value = res.result?.total || 0;
|
||||
}
|
||||
this.pickerSubmitting = true;
|
||||
saveBatchLiveCoupon(payload)
|
||||
.then((res) => {
|
||||
this.pickerSubmitting = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("添加成功");
|
||||
this.pickerVisible = false;
|
||||
this.loadCoupons();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.pickerSubmitting = false;
|
||||
});
|
||||
},
|
||||
handleRemove(record) {
|
||||
this.$Modal.confirm({
|
||||
title: "确认移除",
|
||||
content: "确定要移除该优惠券吗?",
|
||||
onOk: () => {
|
||||
return removeBatchLiveCoupon([record.id]).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("移除成功");
|
||||
this.loadCoupons();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
handleBatchRemove() {
|
||||
this.$Modal.confirm({
|
||||
title: "确认批量移除",
|
||||
content: `确定要移除选中的 ${this.selectedIds.length} 个优惠券吗?`,
|
||||
onOk: () => {
|
||||
return removeBatchLiveCoupon(this.selectedIds).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("移除成功");
|
||||
this.selectedIds = [];
|
||||
this.loadCoupons();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
handleRecommend(record) {
|
||||
setRecommendLiveCoupon(record.id).then((res) => {
|
||||
})
|
||||
.catch(() => {
|
||||
pickerLoading.value = false;
|
||||
});
|
||||
}
|
||||
function onPickerPageSizeChange() {
|
||||
pickerSearch.pageNumber = 1;
|
||||
loadPickerCoupons();
|
||||
}
|
||||
function handlePickerSelectionChange(rows) {
|
||||
pickerSelected.value = rows || [];
|
||||
}
|
||||
function buildLiveCouponPayload(item) {
|
||||
return {
|
||||
couponId: item.id,
|
||||
couponPrice: Number(item.price ?? 0),
|
||||
couponName: item.couponName || "",
|
||||
liveRoomId: props.liveId,
|
||||
liveRoomName: props.liveRoomName || "",
|
||||
hideFlag: false,
|
||||
recommend: false,
|
||||
};
|
||||
}
|
||||
function confirmPicker() {
|
||||
if (!pickerSelected.value.length) {
|
||||
Message.warning("请选择优惠券");
|
||||
return;
|
||||
}
|
||||
const existingCouponIds = new Set(couponList.value.map((c) => String(c.couponId)));
|
||||
const payload = pickerSelected.value
|
||||
.filter((item) => !existingCouponIds.has(String(item.id)))
|
||||
.map((item) => buildLiveCouponPayload(item));
|
||||
if (!payload.length) {
|
||||
Message.warning("所选优惠券均已添加");
|
||||
return;
|
||||
}
|
||||
pickerSubmitting.value = true;
|
||||
saveBatchLiveCoupon(payload)
|
||||
.then((res) => {
|
||||
pickerSubmitting.value = false;
|
||||
if (res.success) {
|
||||
Message.success("添加成功");
|
||||
pickerVisible.value = false;
|
||||
loadCoupons();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
pickerSubmitting.value = false;
|
||||
});
|
||||
}
|
||||
function handleRemove(record) {
|
||||
Modal.confirm({
|
||||
title: "确认移除",
|
||||
content: "确定要移除该优惠券吗?",
|
||||
onOk: () => {
|
||||
return removeBatchLiveCoupon([record.id]).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("已设为推荐");
|
||||
this.loadCoupons();
|
||||
Message.success("移除成功");
|
||||
loadCoupons();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleCancelRecommend(record) {
|
||||
cancelRecommendLiveCoupon(record.id).then((res) => {
|
||||
});
|
||||
}
|
||||
function handleBatchRemove() {
|
||||
Modal.confirm({
|
||||
title: "确认批量移除",
|
||||
content: `确定要移除选中的 ${selectedIds.value.length} 个优惠券吗?`,
|
||||
onOk: () => {
|
||||
return removeBatchLiveCoupon(selectedIds.value).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("已取消推荐");
|
||||
this.loadCoupons();
|
||||
Message.success("移除成功");
|
||||
selectedIds.value = [];
|
||||
loadCoupons();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
function handleRecommend(record) {
|
||||
setRecommendLiveCoupon(record.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("已设为推荐");
|
||||
loadCoupons();
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleCancelRecommend(record) {
|
||||
cancelRecommendLiveCoupon(record.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("已取消推荐");
|
||||
loadCoupons();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -30,136 +30,155 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, onBeforeUnmount, ref, toRef, watch } from "vue";
|
||||
import Cookies from "js-cookie";
|
||||
import { toRef } from "vue";
|
||||
import { useChat } from "../composables/useChat";
|
||||
import { resolveImConfigFromDetail } from "../composables/useLiveSetting";
|
||||
|
||||
export default {
|
||||
defineOptions({
|
||||
name: "LiveChatPanel",
|
||||
props: {
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
liveDetail: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
setup(props) {
|
||||
let userInfo = {};
|
||||
try {
|
||||
userInfo = JSON.parse(Cookies.get("userInfoManager") || "{}");
|
||||
} catch {
|
||||
userInfo = {};
|
||||
liveDetail: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
|
||||
let userInfo = {};
|
||||
try {
|
||||
userInfo = JSON.parse(Cookies.get("userInfoManager") || "{}");
|
||||
} catch {
|
||||
userInfo = {};
|
||||
}
|
||||
|
||||
const {
|
||||
messageList,
|
||||
chatListRef,
|
||||
bottomAnchorRef,
|
||||
unreadCount,
|
||||
isLoadingHistory,
|
||||
initError,
|
||||
initTencentIm,
|
||||
scrollToBottomAndClearUnread,
|
||||
onScroll,
|
||||
setGroupID,
|
||||
cleanup,
|
||||
} = useChat(toRef(props, "liveId"), userInfo, toRef(props, "liveDetail"));
|
||||
|
||||
const initializing = ref(false);
|
||||
const chatInited = ref(false);
|
||||
|
||||
/** 腾讯云 IM 群组:优先后端字段,否则用直播间 ID(与旧版中控台一致) */
|
||||
const imGroupId = computed(() => {
|
||||
return props.liveDetail?.imGroupId || props.liveDetail?.groupId || props.liveId || "";
|
||||
});
|
||||
const imSdkAppId = computed(() => {
|
||||
return resolveImConfigFromDetail(props.liveDetail).sdkAppId;
|
||||
});
|
||||
const canInitChat = computed(() => {
|
||||
const { sdkAppId, secretKey, userSig } = resolveImConfigFromDetail(props.liveDetail);
|
||||
return !!(imGroupId.value && sdkAppId && (userSig || secretKey));
|
||||
});
|
||||
|
||||
watch(
|
||||
canInitChat,
|
||||
(ready) => {
|
||||
if (!ready) return;
|
||||
setGroupID(imGroupId.value);
|
||||
if (props.active) {
|
||||
bootstrapChat();
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
return useChat(toRef(props, "liveId"), userInfo, toRef(props, "liveDetail"));
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
initializing: false,
|
||||
chatInited: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
/** 腾讯云 IM 群组:优先后端字段,否则用直播间 ID(与旧版中控台一致) */
|
||||
imGroupId() {
|
||||
return this.liveDetail?.imGroupId || this.liveDetail?.groupId || this.liveId || "";
|
||||
},
|
||||
imSdkAppId() {
|
||||
return resolveImConfigFromDetail(this.liveDetail).sdkAppId;
|
||||
},
|
||||
canInitChat() {
|
||||
const { sdkAppId, secretKey, userSig } = resolveImConfigFromDetail(this.liveDetail);
|
||||
return !!(this.imGroupId && sdkAppId && (userSig || secretKey));
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
canInitChat: {
|
||||
immediate: true,
|
||||
handler(ready) {
|
||||
if (!ready) return;
|
||||
this.setGroupID(this.imGroupId);
|
||||
if (this.active) {
|
||||
this.bootstrapChat();
|
||||
}
|
||||
},
|
||||
},
|
||||
imGroupId(id) {
|
||||
if (!id) return;
|
||||
this.setGroupID(id);
|
||||
},
|
||||
active(val) {
|
||||
if (val && this.canInitChat && !this.chatInited) {
|
||||
this.bootstrapChat();
|
||||
}
|
||||
},
|
||||
liveId() {
|
||||
this.chatInited = false;
|
||||
this.cleanup();
|
||||
if (this.active && this.canInitChat) {
|
||||
this.bootstrapChat();
|
||||
}
|
||||
},
|
||||
liveDetail: {
|
||||
deep: true,
|
||||
handler(detail, oldDetail) {
|
||||
const nextConfig = resolveImConfigFromDetail(detail);
|
||||
const prevConfig = resolveImConfigFromDetail(oldDetail);
|
||||
if (
|
||||
nextConfig.sdkAppId &&
|
||||
(nextConfig.sdkAppId !== prevConfig.sdkAppId ||
|
||||
nextConfig.secretKey !== prevConfig.secretKey ||
|
||||
nextConfig.userSig !== prevConfig.userSig)
|
||||
) {
|
||||
this.chatInited = false;
|
||||
this.cleanup();
|
||||
if (this.active && this.canInitChat) {
|
||||
this.bootstrapChat();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.cleanup();
|
||||
},
|
||||
methods: {
|
||||
async bootstrapChat() {
|
||||
if (!this.liveId || this.chatInited || this.initializing || !this.canInitChat) {
|
||||
return;
|
||||
}
|
||||
watch(imGroupId, (id) => {
|
||||
if (!id) return;
|
||||
setGroupID(id);
|
||||
});
|
||||
|
||||
const groupId = this.imGroupId;
|
||||
if (!groupId) {
|
||||
this.initError = "缺少直播间 ID,无法初始化聊天";
|
||||
return;
|
||||
}
|
||||
this.setGroupID(groupId);
|
||||
watch(
|
||||
() => props.active,
|
||||
(val) => {
|
||||
if (val && canInitChat.value && !chatInited.value) {
|
||||
bootstrapChat();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
this.initializing = true;
|
||||
try {
|
||||
await this.initTencentIm();
|
||||
if (!this.initError) {
|
||||
this.chatInited = true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("初始化直播聊天失败:", error);
|
||||
this.initError = "聊天初始化失败,请刷新重试";
|
||||
this.chatInited = false;
|
||||
} finally {
|
||||
this.initializing = false;
|
||||
watch(
|
||||
() => props.liveId,
|
||||
() => {
|
||||
chatInited.value = false;
|
||||
cleanup();
|
||||
if (props.active && canInitChat.value) {
|
||||
bootstrapChat();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.liveDetail,
|
||||
(detail, oldDetail) => {
|
||||
const nextConfig = resolveImConfigFromDetail(detail);
|
||||
const prevConfig = resolveImConfigFromDetail(oldDetail);
|
||||
if (
|
||||
nextConfig.sdkAppId &&
|
||||
(nextConfig.sdkAppId !== prevConfig.sdkAppId ||
|
||||
nextConfig.secretKey !== prevConfig.secretKey ||
|
||||
nextConfig.userSig !== prevConfig.userSig)
|
||||
) {
|
||||
chatInited.value = false;
|
||||
cleanup();
|
||||
if (props.active && canInitChat.value) {
|
||||
bootstrapChat();
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
async function bootstrapChat() {
|
||||
if (!props.liveId || chatInited.value || initializing.value || !canInitChat.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const groupId = imGroupId.value;
|
||||
if (!groupId) {
|
||||
initError.value = "缺少直播间 ID,无法初始化聊天";
|
||||
return;
|
||||
}
|
||||
setGroupID(groupId);
|
||||
|
||||
initializing.value = true;
|
||||
try {
|
||||
await initTencentIm();
|
||||
if (!initError.value) {
|
||||
chatInited.value = true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("初始化直播聊天失败:", error);
|
||||
initError.value = "聊天初始化失败,请刷新重试";
|
||||
chatInited.value = false;
|
||||
} finally {
|
||||
initializing.value = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
</div>
|
||||
<el-empty v-if="!loading && !goodsList.length" description="暂无直播商品" />
|
||||
|
||||
<sku-select ref="skuSelect" @selectedGoodsData="selectedGoodsData" />
|
||||
<sku-select ref="skuSelectRef" @selectedGoodsData="selectedGoodsData" />
|
||||
|
||||
<el-dialog v-model="popularityVisible" title="设置商品热度" width="400px">
|
||||
<el-form label-width="80px">
|
||||
@@ -92,7 +92,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, watch } from "vue";
|
||||
import {
|
||||
cancelRecommendLiveGoods,
|
||||
getLiveGoodsList,
|
||||
@@ -104,193 +105,208 @@ import {
|
||||
setRecommendLiveGoods,
|
||||
} from "@/api/live";
|
||||
import skuSelect from "@/components/lili-dialog";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
import { unitPrice } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "LiveGoods",
|
||||
components: { skuSelect },
|
||||
props: {
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
defineOptions({ name: "LiveGoods" });
|
||||
|
||||
const props = defineProps({
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
goodsList: [],
|
||||
selectedIds: [],
|
||||
popularityVisible: false,
|
||||
currentGoods: null,
|
||||
popularityForm: {
|
||||
popularity: 0,
|
||||
},
|
||||
};
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
watch: {
|
||||
liveId() {
|
||||
this.loadGoods();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadGoods();
|
||||
},
|
||||
methods: {
|
||||
formatPrice(price) {
|
||||
return this.$filters.unitPrice(price, "¥");
|
||||
},
|
||||
isSellOut(row) {
|
||||
return !!row.soldOutFlag;
|
||||
},
|
||||
buildLiveGoodsPayload(item) {
|
||||
const price = Number(item.price ?? 0);
|
||||
const originPrice = Number(
|
||||
item.originPrice ?? item.cost ?? item.originalPrice ?? price
|
||||
);
|
||||
return {
|
||||
liveRoomId: this.liveId,
|
||||
goodsId: item.goodsId,
|
||||
skuId: item.id || item.skuId,
|
||||
goodsName: item.goodsName || "",
|
||||
stock: String(item.quantity ?? item.stock ?? ""),
|
||||
price,
|
||||
thumbnail: item.thumbnail || "",
|
||||
salesCount: Number(item.salesCount ?? item.buyCount ?? 0),
|
||||
storeId: item.storeId || "",
|
||||
storeName: item.storeName || "",
|
||||
originPrice,
|
||||
sellPoint: item.sellPoint || item.sellingPoint || "",
|
||||
popularity: 0,
|
||||
hideFlag: false,
|
||||
canBuyFlag: true,
|
||||
soldOutFlag: false,
|
||||
recommend: false,
|
||||
};
|
||||
},
|
||||
loadGoods() {
|
||||
if (!this.liveId) return;
|
||||
this.loading = true;
|
||||
getLiveGoodsList(this.liveId)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.goodsList = res.result || [];
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
openChooseGoods() {
|
||||
this.$refs.skuSelect.open("goods");
|
||||
const data = JSON.parse(JSON.stringify(this.goodsList));
|
||||
data.forEach((e) => {
|
||||
if (e.skuId) e.id = e.skuId;
|
||||
});
|
||||
this.$refs.skuSelect.goodsData = data;
|
||||
},
|
||||
selectedGoodsData(selected) {
|
||||
if (!selected?.length) return;
|
||||
const existingSkuIds = new Set(this.goodsList.map((g) => String(g.skuId)));
|
||||
const payload = selected
|
||||
.filter((item) => !existingSkuIds.has(String(item.id || item.skuId)))
|
||||
.map((item) => this.buildLiveGoodsPayload(item));
|
||||
if (!payload.length) {
|
||||
this.$Message.warning("所选商品均已添加");
|
||||
return;
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const goodsList = ref([]);
|
||||
const selectedIds = ref([]);
|
||||
const popularityVisible = ref(false);
|
||||
const currentGoods = ref(null);
|
||||
const popularityForm = reactive({
|
||||
popularity: 0,
|
||||
});
|
||||
|
||||
// 模板 ref:原 ref="skuSelect" 与 import 的组件同名,改名避免互相覆盖
|
||||
const skuSelectRef = ref(null);
|
||||
|
||||
watch(
|
||||
() => props.liveId,
|
||||
() => {
|
||||
loadGoods();
|
||||
}
|
||||
);
|
||||
|
||||
function formatPrice(price) {
|
||||
return unitPrice(price, "¥");
|
||||
}
|
||||
|
||||
function isSellOut(row) {
|
||||
return !!row.soldOutFlag;
|
||||
}
|
||||
|
||||
function buildLiveGoodsPayload(item) {
|
||||
const price = Number(item.price ?? 0);
|
||||
const originPrice = Number(
|
||||
item.originPrice ?? item.cost ?? item.originalPrice ?? price
|
||||
);
|
||||
return {
|
||||
liveRoomId: props.liveId,
|
||||
goodsId: item.goodsId,
|
||||
skuId: item.id || item.skuId,
|
||||
goodsName: item.goodsName || "",
|
||||
stock: String(item.quantity ?? item.stock ?? ""),
|
||||
price,
|
||||
thumbnail: item.thumbnail || "",
|
||||
salesCount: Number(item.salesCount ?? item.buyCount ?? 0),
|
||||
storeId: item.storeId || "",
|
||||
storeName: item.storeName || "",
|
||||
originPrice,
|
||||
sellPoint: item.sellPoint || item.sellingPoint || "",
|
||||
popularity: 0,
|
||||
hideFlag: false,
|
||||
canBuyFlag: true,
|
||||
soldOutFlag: false,
|
||||
recommend: false,
|
||||
};
|
||||
}
|
||||
|
||||
function loadGoods() {
|
||||
if (!props.liveId) return;
|
||||
loading.value = true;
|
||||
getLiveGoodsList(props.liveId)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
goodsList.value = res.result || [];
|
||||
}
|
||||
saveBatchLiveGoods(payload).then((res) => {
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function openChooseGoods() {
|
||||
skuSelectRef.value.open("goods");
|
||||
const data = JSON.parse(JSON.stringify(goodsList.value));
|
||||
data.forEach((e) => {
|
||||
if (e.skuId) e.id = e.skuId;
|
||||
});
|
||||
skuSelectRef.value.goodsData = data;
|
||||
}
|
||||
|
||||
function selectedGoodsData(selected) {
|
||||
if (!selected?.length) return;
|
||||
const existingSkuIds = new Set(goodsList.value.map((g) => String(g.skuId)));
|
||||
const payload = selected
|
||||
.filter((item) => !existingSkuIds.has(String(item.id || item.skuId)))
|
||||
.map((item) => buildLiveGoodsPayload(item));
|
||||
if (!payload.length) {
|
||||
Message.warning("所选商品均已添加");
|
||||
return;
|
||||
}
|
||||
saveBatchLiveGoods(payload).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("添加成功");
|
||||
loadGoods();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleSelectionChange(rows) {
|
||||
selectedIds.value = (rows || []).map((r) => r.id);
|
||||
}
|
||||
|
||||
function handleRemove(record) {
|
||||
Modal.confirm({
|
||||
title: "确认移除",
|
||||
content: "确定要移除该商品吗?",
|
||||
onOk: () => {
|
||||
return removeBatchLiveGoods([record.id]).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("添加成功");
|
||||
this.loadGoods();
|
||||
Message.success("移除成功");
|
||||
loadGoods();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleSelectionChange(rows) {
|
||||
this.selectedIds = (rows || []).map((r) => r.id);
|
||||
},
|
||||
handleRemove(record) {
|
||||
this.$Modal.confirm({
|
||||
title: "确认移除",
|
||||
content: "确定要移除该商品吗?",
|
||||
onOk: () => {
|
||||
return removeBatchLiveGoods([record.id]).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("移除成功");
|
||||
this.loadGoods();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
handleBatchRemove() {
|
||||
this.$Modal.confirm({
|
||||
title: "确认批量移除",
|
||||
content: `确定要移除选中的 ${this.selectedIds.length} 个商品吗?`,
|
||||
onOk: () => {
|
||||
return removeBatchLiveGoods(this.selectedIds).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("移除成功");
|
||||
this.selectedIds = [];
|
||||
this.loadGoods();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
handleRecommend(record) {
|
||||
setRecommendLiveGoods(record.id).then((res) => {
|
||||
});
|
||||
}
|
||||
|
||||
function handleBatchRemove() {
|
||||
Modal.confirm({
|
||||
title: "确认批量移除",
|
||||
content: `确定要移除选中的 ${selectedIds.value.length} 个商品吗?`,
|
||||
onOk: () => {
|
||||
return removeBatchLiveGoods(selectedIds.value).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("已设为推荐");
|
||||
this.loadGoods();
|
||||
Message.success("移除成功");
|
||||
selectedIds.value = [];
|
||||
loadGoods();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleCancelRecommend(record) {
|
||||
cancelRecommendLiveGoods(record.id).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("已取消推荐");
|
||||
this.loadGoods();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleToggleSoldOut(record) {
|
||||
const soldOutFlag = !this.isSellOut(record);
|
||||
setLiveGoodsSoldOut(record.id, soldOutFlag).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success(soldOutFlag ? "已设为售罄" : "已取消售罄");
|
||||
this.loadGoods();
|
||||
}
|
||||
});
|
||||
},
|
||||
openPopularityModal(record) {
|
||||
this.currentGoods = record;
|
||||
getLiveGoodsPopularity(record.id, this.liveId).then((res) => {
|
||||
this.popularityForm.popularity = res.success
|
||||
? res.result?.popularity ?? record.popularity ?? 0
|
||||
: record.popularity ?? 0;
|
||||
this.popularityVisible = true;
|
||||
});
|
||||
},
|
||||
handleSavePopularity() {
|
||||
if (!this.currentGoods) return;
|
||||
setLiveGoodsPopularity(
|
||||
this.currentGoods.id,
|
||||
this.liveId,
|
||||
this.popularityForm.popularity
|
||||
).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("热度设置成功");
|
||||
this.popularityVisible = false;
|
||||
this.loadGoods();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function handleRecommend(record) {
|
||||
setRecommendLiveGoods(record.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("已设为推荐");
|
||||
loadGoods();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleCancelRecommend(record) {
|
||||
cancelRecommendLiveGoods(record.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("已取消推荐");
|
||||
loadGoods();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleToggleSoldOut(record) {
|
||||
const soldOutFlag = !isSellOut(record);
|
||||
setLiveGoodsSoldOut(record.id, soldOutFlag).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success(soldOutFlag ? "已设为售罄" : "已取消售罄");
|
||||
loadGoods();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function openPopularityModal(record) {
|
||||
currentGoods.value = record;
|
||||
getLiveGoodsPopularity(record.id, props.liveId).then((res) => {
|
||||
popularityForm.popularity = res.success
|
||||
? res.result?.popularity ?? record.popularity ?? 0
|
||||
: record.popularity ?? 0;
|
||||
popularityVisible.value = true;
|
||||
});
|
||||
}
|
||||
|
||||
function handleSavePopularity() {
|
||||
if (!currentGoods.value) return;
|
||||
setLiveGoodsPopularity(
|
||||
currentGoods.value.id,
|
||||
props.liveId,
|
||||
popularityForm.popularity
|
||||
).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("热度设置成功");
|
||||
popularityVisible.value = false;
|
||||
loadGoods();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadGoods();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -76,142 +76,144 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, watch } from "vue";
|
||||
import { DocumentCopy } from "@element-plus/icons-vue";
|
||||
import { queryLiveOrderPage } from "@/api/live";
|
||||
import { Message } from "@/utils/message";
|
||||
import { unixToDate, unitPrice } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "LivePurchaseRecords",
|
||||
components: { DocumentCopy },
|
||||
props: {
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
defineOptions({ name: "LivePurchaseRecords" });
|
||||
|
||||
const props = defineProps({
|
||||
liveId: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
orderList: [],
|
||||
pagination: {
|
||||
pageNumber: 1,
|
||||
pageSize: 5,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const loading = ref(false);
|
||||
const orderList = ref([]);
|
||||
const pagination = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 5,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.liveId,
|
||||
() => {
|
||||
pagination.pageNumber = 1;
|
||||
loadData();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
|
||||
function refresh() {
|
||||
loadData();
|
||||
}
|
||||
function formatTime(time) {
|
||||
if (!time) return "-";
|
||||
if (typeof time === "string" && time.includes("-")) return time;
|
||||
const ts = typeof time === "number" ? (time > 1e12 ? time / 1000 : time) : time;
|
||||
return unixToDate(ts);
|
||||
}
|
||||
function formatPrice(val) {
|
||||
return unitPrice(val, "¥");
|
||||
}
|
||||
function getOrderItems(order) {
|
||||
if (order.orderItems?.length) return order.orderItems;
|
||||
if (order.groupGoodsId) {
|
||||
return [
|
||||
{
|
||||
image: order.groupImages,
|
||||
name: order.groupName,
|
||||
goodsPrice: order.groupGoodsPrice,
|
||||
num: order.groupNum,
|
||||
},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
liveId() {
|
||||
this.pagination.pageNumber = 1;
|
||||
this.loadData();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.loadData();
|
||||
},
|
||||
methods: {
|
||||
refresh() {
|
||||
this.loadData();
|
||||
},
|
||||
formatTime(time) {
|
||||
if (!time) return "-";
|
||||
if (typeof time === "string" && time.includes("-")) return time;
|
||||
const ts = typeof time === "number" ? (time > 1e12 ? time / 1000 : time) : time;
|
||||
return this.$filters.unixToDate(ts);
|
||||
},
|
||||
formatPrice(val) {
|
||||
return this.$filters.unitPrice(val, "¥");
|
||||
},
|
||||
getOrderItems(order) {
|
||||
if (order.orderItems?.length) return order.orderItems;
|
||||
if (order.groupGoodsId) {
|
||||
return [
|
||||
{
|
||||
image: order.groupImages,
|
||||
name: order.groupName,
|
||||
goodsPrice: order.groupGoodsPrice,
|
||||
num: order.groupNum,
|
||||
},
|
||||
];
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
function clientTypeText(v) {
|
||||
const map = { H5: "移动端", PC: "PC端", WECHAT_MP: "小程序端", APP: "移动应用端" };
|
||||
return map[v] || v || "-";
|
||||
}
|
||||
function clientTypeDotClass(v) {
|
||||
const map = { H5: "warning", PC: "primary", WECHAT_MP: "success", APP: "info" };
|
||||
return map[v] || "info";
|
||||
}
|
||||
function orderPromotionText(v) {
|
||||
const map = {
|
||||
NORMAL: "普通订单",
|
||||
PINTUAN: "拼团订单",
|
||||
GIFT: "赠品订单",
|
||||
POINTS: "积分订单",
|
||||
KANJIA: "砍价订单",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
}
|
||||
function orderStatusText(v) {
|
||||
const map = {
|
||||
UNPAID: "未付款",
|
||||
PAID: "已付款",
|
||||
UNDELIVERED: "待发货",
|
||||
STAY_PICKED_UP: "待自提",
|
||||
PARTS_DELIVERED: "部分发货",
|
||||
DELIVERED: "已发货",
|
||||
COMPLETED: "已完成",
|
||||
TAKE: "待核验",
|
||||
CANCELLED: "已取消",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
}
|
||||
function orderStatusDotClass(v) {
|
||||
const map = {
|
||||
UNPAID: "danger",
|
||||
PAID: "primary",
|
||||
UNDELIVERED: "info",
|
||||
STAY_PICKED_UP: "info",
|
||||
PARTS_DELIVERED: "warning",
|
||||
DELIVERED: "warning",
|
||||
COMPLETED: "success",
|
||||
TAKE: "warning",
|
||||
CANCELLED: "danger",
|
||||
};
|
||||
return map[v] || "info";
|
||||
}
|
||||
function copySn(sn) {
|
||||
if (!sn) return;
|
||||
navigator.clipboard.writeText(sn);
|
||||
Message.success("订单编号已复制");
|
||||
}
|
||||
function onPageSizeChange() {
|
||||
pagination.pageNumber = 1;
|
||||
loadData();
|
||||
}
|
||||
function loadData() {
|
||||
if (!props.liveId) return;
|
||||
loading.value = true;
|
||||
queryLiveOrderPage({
|
||||
liveRoomId: props.liveId,
|
||||
pageNumber: pagination.pageNumber,
|
||||
pageSize: pagination.pageSize,
|
||||
})
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
orderList.value = res.result?.records || [];
|
||||
pagination.total = res.result?.total || 0;
|
||||
}
|
||||
return [];
|
||||
},
|
||||
clientTypeText(v) {
|
||||
const map = { H5: "移动端", PC: "PC端", WECHAT_MP: "小程序端", APP: "移动应用端" };
|
||||
return map[v] || v || "-";
|
||||
},
|
||||
clientTypeDotClass(v) {
|
||||
const map = { H5: "warning", PC: "primary", WECHAT_MP: "success", APP: "info" };
|
||||
return map[v] || "info";
|
||||
},
|
||||
orderPromotionText(v) {
|
||||
const map = {
|
||||
NORMAL: "普通订单",
|
||||
PINTUAN: "拼团订单",
|
||||
GIFT: "赠品订单",
|
||||
POINTS: "积分订单",
|
||||
KANJIA: "砍价订单",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
},
|
||||
orderStatusText(v) {
|
||||
const map = {
|
||||
UNPAID: "未付款",
|
||||
PAID: "已付款",
|
||||
UNDELIVERED: "待发货",
|
||||
STAY_PICKED_UP: "待自提",
|
||||
PARTS_DELIVERED: "部分发货",
|
||||
DELIVERED: "已发货",
|
||||
COMPLETED: "已完成",
|
||||
TAKE: "待核验",
|
||||
CANCELLED: "已取消",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
},
|
||||
orderStatusDotClass(v) {
|
||||
const map = {
|
||||
UNPAID: "danger",
|
||||
PAID: "primary",
|
||||
UNDELIVERED: "info",
|
||||
STAY_PICKED_UP: "info",
|
||||
PARTS_DELIVERED: "warning",
|
||||
DELIVERED: "warning",
|
||||
COMPLETED: "success",
|
||||
TAKE: "warning",
|
||||
CANCELLED: "danger",
|
||||
};
|
||||
return map[v] || "info";
|
||||
},
|
||||
copySn(sn) {
|
||||
if (!sn) return;
|
||||
navigator.clipboard.writeText(sn);
|
||||
this.$Message.success("订单编号已复制");
|
||||
},
|
||||
onPageSizeChange() {
|
||||
this.pagination.pageNumber = 1;
|
||||
this.loadData();
|
||||
},
|
||||
loadData() {
|
||||
if (!this.liveId) return;
|
||||
this.loading = true;
|
||||
queryLiveOrderPage({
|
||||
liveRoomId: this.liveId,
|
||||
pageNumber: this.pagination.pageNumber,
|
||||
pageSize: this.pagination.pageSize,
|
||||
})
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.orderList = res.result?.records || [];
|
||||
this.pagination.total = res.result?.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
defineExpose({ refresh });
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -238,7 +238,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import {
|
||||
endLive,
|
||||
getLiveInfo,
|
||||
@@ -248,450 +250,450 @@ import {
|
||||
} from "@/api/live";
|
||||
import config from "@/config";
|
||||
import { DocumentCopy, Refresh } from "@element-plus/icons-vue";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
import { unixToDate } from "@/utils/filters";
|
||||
import CommentReview from "./components/CommentReview.vue";
|
||||
import Coupon from "./components/Coupon.vue";
|
||||
import LiveChatPanel from "./components/LiveChatPanel.vue";
|
||||
import LiveGoods from "./components/LiveGoods.vue";
|
||||
import LivePurchaseRecords from "./components/LivePurchaseRecords.vue";
|
||||
|
||||
export default {
|
||||
name: "live-control-panel",
|
||||
components: { CommentReview, Coupon, LiveChatPanel, LiveGoods, LivePurchaseRecords, DocumentCopy, Refresh },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
liveId: "",
|
||||
videoUrl: "",
|
||||
wapUrl: "",
|
||||
aspectRatio: "16:9",
|
||||
chatTab: "chat",
|
||||
statsTab: "general",
|
||||
manageTab: "comment",
|
||||
liveDetail: {},
|
||||
orderStats: {},
|
||||
onlineUserList: [],
|
||||
onlineLoading: false,
|
||||
onlinePagination: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
},
|
||||
nowTick: Date.now(),
|
||||
durationTimer: null,
|
||||
pollTimer: null,
|
||||
hlsHttpFallbackUsed: false,
|
||||
lastHlsPullUrl: "",
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.hlsInstance = null;
|
||||
this.hlsVideoElement = null;
|
||||
this._hlsVideoClickHandler = null;
|
||||
},
|
||||
computed: {
|
||||
liveStatus() {
|
||||
return this.liveDetail.liveStatus || this.liveDetail.status || "";
|
||||
},
|
||||
isLiving() {
|
||||
return this.liveStatus === "LIVING";
|
||||
},
|
||||
shouldPlayStream() {
|
||||
return this.isLiving && !!this.videoUrl;
|
||||
},
|
||||
showPlayerCover() {
|
||||
return !this.shouldPlayStream;
|
||||
},
|
||||
cumulativeViewers() {
|
||||
return Number(this.liveDetail.actualViewNumber ?? 0);
|
||||
},
|
||||
currentViewers() {
|
||||
return this.cumulativeViewers;
|
||||
},
|
||||
commentPeopleNumber() {
|
||||
return Number(this.liveDetail.commentPeopleNumber ?? 0);
|
||||
},
|
||||
interactionRate() {
|
||||
if (!this.cumulativeViewers) return "0.00%";
|
||||
return `${((this.commentPeopleNumber / this.cumulativeViewers) * 100).toFixed(2)}%`;
|
||||
},
|
||||
dealConversionRate() {
|
||||
if (!this.cumulativeViewers) return "0%";
|
||||
const dealCount = Number(this.orderStats.dealUserCount ?? 0);
|
||||
return `${((dealCount / this.cumulativeViewers) * 100).toFixed(0)}%`;
|
||||
},
|
||||
liveDuration() {
|
||||
void this.nowTick;
|
||||
const startMs = this.parseDateTime(this.liveDetail.startTime);
|
||||
if (!startMs || Date.now() < startMs) return "00:00:00";
|
||||
let endMs = Date.now();
|
||||
if (this.liveStatus === "ENDED") {
|
||||
const endTimeMs = this.parseDateTime(this.liveDetail.endTime);
|
||||
if (endTimeMs && endTimeMs > startMs) {
|
||||
endMs = endTimeMs;
|
||||
}
|
||||
defineOptions({ name: "live-control-panel" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const purchaseRecords = ref(null);
|
||||
const hlsContainer = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const liveId = ref("");
|
||||
const videoUrl = ref("");
|
||||
const wapUrl = ref("");
|
||||
const aspectRatio = ref("16:9");
|
||||
const chatTab = ref("chat");
|
||||
const statsTab = ref("general");
|
||||
const manageTab = ref("comment");
|
||||
const liveDetail = ref({});
|
||||
const orderStats = ref({});
|
||||
const onlineUserList = ref([]);
|
||||
const onlineLoading = ref(false);
|
||||
const onlinePagination = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const nowTick = ref(Date.now());
|
||||
const durationTimer = ref(null);
|
||||
const pollTimer = ref(null);
|
||||
const hlsHttpFallbackUsed = ref(false);
|
||||
const lastHlsPullUrl = ref("");
|
||||
|
||||
// 原 created 中直接挂到 this 上的字段(未在 data 声明),保持非响应式
|
||||
let hlsInstance = null;
|
||||
let hlsVideoElement = null;
|
||||
let _hlsVideoClickHandler = null;
|
||||
|
||||
const liveStatus = computed(() => {
|
||||
return liveDetail.value.liveStatus || liveDetail.value.status || "";
|
||||
});
|
||||
const isLiving = computed(() => {
|
||||
return liveStatus.value === "LIVING";
|
||||
});
|
||||
const shouldPlayStream = computed(() => {
|
||||
return isLiving.value && !!videoUrl.value;
|
||||
});
|
||||
const showPlayerCover = computed(() => {
|
||||
return !shouldPlayStream.value;
|
||||
});
|
||||
const cumulativeViewers = computed(() => {
|
||||
return Number(liveDetail.value.actualViewNumber ?? 0);
|
||||
});
|
||||
const currentViewers = computed(() => {
|
||||
return cumulativeViewers.value;
|
||||
});
|
||||
const commentPeopleNumber = computed(() => {
|
||||
return Number(liveDetail.value.commentPeopleNumber ?? 0);
|
||||
});
|
||||
const interactionRate = computed(() => {
|
||||
if (!cumulativeViewers.value) return "0.00%";
|
||||
return `${((commentPeopleNumber.value / cumulativeViewers.value) * 100).toFixed(2)}%`;
|
||||
});
|
||||
const dealConversionRate = computed(() => {
|
||||
if (!cumulativeViewers.value) return "0%";
|
||||
const dealCount = Number(orderStats.value.dealUserCount ?? 0);
|
||||
return `${((dealCount / cumulativeViewers.value) * 100).toFixed(0)}%`;
|
||||
});
|
||||
const liveDuration = computed(() => {
|
||||
void nowTick.value;
|
||||
const startMs = parseDateTime(liveDetail.value.startTime);
|
||||
if (!startMs || Date.now() < startMs) return "00:00:00";
|
||||
let endMs = Date.now();
|
||||
if (liveStatus.value === "ENDED") {
|
||||
const endTimeMs = parseDateTime(liveDetail.value.endTime);
|
||||
if (endTimeMs && endTimeMs > startMs) {
|
||||
endMs = endTimeMs;
|
||||
}
|
||||
}
|
||||
return formatDuration(endMs - startMs);
|
||||
});
|
||||
|
||||
watch(videoUrl, () => {
|
||||
syncH5Player();
|
||||
});
|
||||
watch(isLiving, () => {
|
||||
syncH5Player();
|
||||
if (isLiving.value) {
|
||||
startPolling();
|
||||
} else {
|
||||
stopPolling();
|
||||
}
|
||||
});
|
||||
watch(aspectRatio, () => {
|
||||
lastHlsPullUrl.value = "";
|
||||
nextTick(() => syncH5Player());
|
||||
});
|
||||
watch(chatTab, (tab) => {
|
||||
if (tab === "online") loadOnlineUsers();
|
||||
});
|
||||
watch(statsTab, (tab) => {
|
||||
if (tab === "purchase") purchaseRecords.value?.refresh();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
liveId.value = String(route.query.id || "");
|
||||
if (!liveId.value) {
|
||||
Message.error("缺少直播ID");
|
||||
return;
|
||||
}
|
||||
refreshAll();
|
||||
startDurationTimer();
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
destroyH5HlsPlayer();
|
||||
stopPolling();
|
||||
stopDurationTimer();
|
||||
});
|
||||
|
||||
function parseDateTime(value) {
|
||||
if (!value) return null;
|
||||
if (typeof value === "string" && value.includes("-")) {
|
||||
return new Date(value.replace(/-/g, "/")).getTime();
|
||||
}
|
||||
if (typeof value === "number") {
|
||||
return value > 1e12 ? value : value * 1000;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function formatDuration(ms) {
|
||||
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
const pad = (n) => String(n).padStart(2, "0");
|
||||
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||||
}
|
||||
function startDurationTimer() {
|
||||
stopDurationTimer();
|
||||
durationTimer.value = setInterval(() => {
|
||||
nowTick.value = Date.now();
|
||||
}, 1000);
|
||||
}
|
||||
function stopDurationTimer() {
|
||||
if (durationTimer.value) {
|
||||
clearInterval(durationTimer.value);
|
||||
durationTimer.value = null;
|
||||
}
|
||||
}
|
||||
function formatTime(time) {
|
||||
if (!time) return "-";
|
||||
if (typeof time === "string" && time.includes("-")) return time;
|
||||
const ts = typeof time === "number" ? (time > 1e12 ? time / 1000 : time) : time;
|
||||
return unixToDate(ts);
|
||||
}
|
||||
function formatAmount(val) {
|
||||
return Number(val || 0).toFixed(2);
|
||||
}
|
||||
function liveStatusText(status) {
|
||||
const map = { NEW: "未开始", LIVING: "直播中", PAUSED: "已暂停", ENDED: "已结束" };
|
||||
return map[status] || "未知";
|
||||
}
|
||||
function liveStatusTagType(status) {
|
||||
const map = { NEW: "info", LIVING: "success", PAUSED: "warning", ENDED: "warning" };
|
||||
return map[status] || "info";
|
||||
}
|
||||
function startPolling() {
|
||||
stopPolling();
|
||||
if (!isLiving.value) return;
|
||||
pollTimer.value = setInterval(() => {
|
||||
loadDetail(false);
|
||||
}, 5000);
|
||||
}
|
||||
function stopPolling() {
|
||||
if (pollTimer.value) {
|
||||
clearInterval(pollTimer.value);
|
||||
pollTimer.value = null;
|
||||
}
|
||||
}
|
||||
function refreshAll() {
|
||||
loadDetail();
|
||||
loadOrderStats();
|
||||
purchaseRecords.value?.refresh();
|
||||
if (chatTab.value === "online") loadOnlineUsers();
|
||||
}
|
||||
function buildHlsPlayUrl(url, useHttpFallback = false) {
|
||||
if (!url) return "";
|
||||
const httpUrl =
|
||||
useHttpFallback && url.startsWith("https://")
|
||||
? url.replace("https://", "http://")
|
||||
: url;
|
||||
return httpUrl.replace(".flv", ".m3u8");
|
||||
}
|
||||
function syncH5Player(retry = 0) {
|
||||
const shouldPlay = shouldPlayStream.value;
|
||||
if (!shouldPlay) {
|
||||
destroyH5HlsPlayer();
|
||||
lastHlsPullUrl.value = "";
|
||||
return;
|
||||
}
|
||||
if (
|
||||
videoUrl.value === lastHlsPullUrl.value &&
|
||||
hlsInstance &&
|
||||
hlsVideoElement &&
|
||||
!hlsVideoElement.paused
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (videoUrl.value === lastHlsPullUrl.value && hlsInstance) {
|
||||
ensureH5VideoPlaying(hlsVideoElement);
|
||||
return;
|
||||
}
|
||||
lastHlsPullUrl.value = videoUrl.value;
|
||||
hlsHttpFallbackUsed.value = false;
|
||||
nextTick(() => {
|
||||
const container = hlsContainer.value;
|
||||
if (!container) {
|
||||
if (retry < 8) {
|
||||
setTimeout(() => syncH5Player(retry + 1), 80);
|
||||
}
|
||||
return this.formatDuration(endMs - startMs);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
videoUrl() {
|
||||
this.syncH5Player();
|
||||
},
|
||||
isLiving() {
|
||||
this.syncH5Player();
|
||||
if (this.isLiving) {
|
||||
this.startPolling();
|
||||
} else {
|
||||
this.stopPolling();
|
||||
}
|
||||
},
|
||||
aspectRatio() {
|
||||
this.lastHlsPullUrl = "";
|
||||
this.$nextTick(() => this.syncH5Player());
|
||||
},
|
||||
chatTab(tab) {
|
||||
if (tab === "online") this.loadOnlineUsers();
|
||||
},
|
||||
statsTab(tab) {
|
||||
if (tab === "purchase") this.$refs.purchaseRecords?.refresh();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.liveId = String(this.$route.query.id || "");
|
||||
if (!this.liveId) {
|
||||
this.$Message.error("缺少直播ID");
|
||||
return;
|
||||
}
|
||||
this.refreshAll();
|
||||
this.startDurationTimer();
|
||||
},
|
||||
beforeUnmount() {
|
||||
this.destroyH5HlsPlayer();
|
||||
this.stopPolling();
|
||||
this.stopDurationTimer();
|
||||
},
|
||||
methods: {
|
||||
parseDateTime(value) {
|
||||
if (!value) return null;
|
||||
if (typeof value === "string" && value.includes("-")) {
|
||||
return new Date(value.replace(/-/g, "/")).getTime();
|
||||
}
|
||||
if (typeof value === "number") {
|
||||
return value > 1e12 ? value : value * 1000;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
formatDuration(ms) {
|
||||
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
|
||||
const hours = Math.floor(totalSeconds / 3600);
|
||||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||||
const seconds = totalSeconds % 60;
|
||||
const pad = (n) => String(n).padStart(2, "0");
|
||||
return `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||||
},
|
||||
startDurationTimer() {
|
||||
this.stopDurationTimer();
|
||||
this.durationTimer = setInterval(() => {
|
||||
this.nowTick = Date.now();
|
||||
}, 1000);
|
||||
},
|
||||
stopDurationTimer() {
|
||||
if (this.durationTimer) {
|
||||
clearInterval(this.durationTimer);
|
||||
this.durationTimer = null;
|
||||
}
|
||||
},
|
||||
formatTime(time) {
|
||||
if (!time) return "-";
|
||||
if (typeof time === "string" && time.includes("-")) return time;
|
||||
const ts = typeof time === "number" ? (time > 1e12 ? time / 1000 : time) : time;
|
||||
return this.$filters.unixToDate(ts);
|
||||
},
|
||||
formatAmount(val) {
|
||||
return Number(val || 0).toFixed(2);
|
||||
},
|
||||
liveStatusText(status) {
|
||||
const map = { NEW: "未开始", LIVING: "直播中", PAUSED: "已暂停", ENDED: "已结束" };
|
||||
return map[status] || "未知";
|
||||
},
|
||||
liveStatusTagType(status) {
|
||||
const map = { NEW: "info", LIVING: "success", PAUSED: "warning", ENDED: "warning" };
|
||||
return map[status] || "info";
|
||||
},
|
||||
startPolling() {
|
||||
this.stopPolling();
|
||||
if (!this.isLiving) return;
|
||||
this.pollTimer = setInterval(() => {
|
||||
this.loadDetail(false);
|
||||
}, 5000);
|
||||
},
|
||||
stopPolling() {
|
||||
if (this.pollTimer) {
|
||||
clearInterval(this.pollTimer);
|
||||
this.pollTimer = null;
|
||||
}
|
||||
},
|
||||
refreshAll() {
|
||||
this.loadDetail();
|
||||
this.loadOrderStats();
|
||||
this.$refs.purchaseRecords?.refresh();
|
||||
if (this.chatTab === "online") this.loadOnlineUsers();
|
||||
},
|
||||
buildHlsPlayUrl(url, useHttpFallback = false) {
|
||||
if (!url) return "";
|
||||
const httpUrl =
|
||||
useHttpFallback && url.startsWith("https://")
|
||||
? url.replace("https://", "http://")
|
||||
: url;
|
||||
return httpUrl.replace(".flv", ".m3u8");
|
||||
},
|
||||
syncH5Player(retry = 0) {
|
||||
const shouldPlay = this.shouldPlayStream;
|
||||
if (!shouldPlay) {
|
||||
this.destroyH5HlsPlayer();
|
||||
this.lastHlsPullUrl = "";
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.videoUrl === this.lastHlsPullUrl &&
|
||||
this.hlsInstance &&
|
||||
this.hlsVideoElement &&
|
||||
!this.hlsVideoElement.paused
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (this.videoUrl === this.lastHlsPullUrl && this.hlsInstance) {
|
||||
this.ensureH5VideoPlaying(this.hlsVideoElement);
|
||||
return;
|
||||
}
|
||||
this.lastHlsPullUrl = this.videoUrl;
|
||||
this.hlsHttpFallbackUsed = false;
|
||||
this.$nextTick(() => {
|
||||
const container = this.$refs.hlsContainer;
|
||||
if (!container) {
|
||||
if (retry < 8) {
|
||||
setTimeout(() => this.syncH5Player(retry + 1), 80);
|
||||
initH5HlsPlayer();
|
||||
});
|
||||
}
|
||||
function ensureH5VideoPlaying(video) {
|
||||
if (!video) return Promise.resolve();
|
||||
const tryPlay = (muted) => {
|
||||
video.muted = muted;
|
||||
return video.play().catch(() => Promise.reject());
|
||||
};
|
||||
return tryPlay(false)
|
||||
.catch(() => tryPlay(true))
|
||||
.catch(() => {
|
||||
console.warn("[HLS] 自动播放被阻止,请点击画面播放");
|
||||
});
|
||||
}
|
||||
async function initH5HlsPlayer(useHttpFallback = false) {
|
||||
const originalUrl = videoUrl.value;
|
||||
const container = hlsContainer.value;
|
||||
if (!originalUrl || !container) return;
|
||||
|
||||
const url = buildHlsPlayUrl(originalUrl, useHttpFallback);
|
||||
destroyH5HlsPlayer();
|
||||
|
||||
const video = document.createElement("video");
|
||||
video.style.cssText = "width:100%;height:100%;object-fit:cover;background:#000;";
|
||||
video.autoplay = true;
|
||||
video.muted = true;
|
||||
video.defaultMuted = true;
|
||||
video.playsInline = true;
|
||||
video.setAttribute("autoplay", "");
|
||||
video.setAttribute("muted", "");
|
||||
video.setAttribute("playsinline", "");
|
||||
video.setAttribute("webkit-playsinline", "");
|
||||
video.controls = false;
|
||||
|
||||
const onVideoClick = () => {
|
||||
if (video.muted) {
|
||||
video.muted = false;
|
||||
}
|
||||
video.play().catch(() => {});
|
||||
};
|
||||
video.addEventListener("click", onVideoClick);
|
||||
container.appendChild(video);
|
||||
hlsVideoElement = video;
|
||||
_hlsVideoClickHandler = onVideoClick;
|
||||
|
||||
const onReadyPlay = () => {
|
||||
ensureH5VideoPlaying(video);
|
||||
};
|
||||
|
||||
let Hls = null;
|
||||
try {
|
||||
const hlsModule = await import("hls.js");
|
||||
Hls = hlsModule.default;
|
||||
} catch (error) {
|
||||
console.error("[HLS] 加载播放器失败:", error);
|
||||
Message.error("直播播放器加载失败,请刷新页面重试");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Hls.isSupported()) {
|
||||
const hls = new Hls({
|
||||
enableWorker: true,
|
||||
lowLatencyMode: true,
|
||||
liveSyncDurationCount: 3,
|
||||
liveMaxLatencyDurationCount: 6,
|
||||
maxBufferLength: 10,
|
||||
maxMaxBufferLength: 30,
|
||||
});
|
||||
hlsInstance = hls;
|
||||
hls.on(Hls.Events.MEDIA_ATTACHED, onReadyPlay);
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, onReadyPlay);
|
||||
hls.attachMedia(video);
|
||||
hls.loadSource(url);
|
||||
hls.on(Hls.Events.ERROR, (_event, data) => {
|
||||
if (!data.fatal) return;
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
if (!hlsHttpFallbackUsed.value && originalUrl.startsWith("https://")) {
|
||||
console.warn("[HLS] HTTPS 加载失败,尝试 HTTP 降级...");
|
||||
hlsHttpFallbackUsed.value = true;
|
||||
destroyH5HlsPlayer();
|
||||
nextTick(() => initH5HlsPlayer(true));
|
||||
} else {
|
||||
console.error("[HLS] 网络错误,尝试重连...");
|
||||
hls.startLoad();
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.initH5HlsPlayer();
|
||||
});
|
||||
},
|
||||
ensureH5VideoPlaying(video) {
|
||||
if (!video) return Promise.resolve();
|
||||
const tryPlay = (muted) => {
|
||||
video.muted = muted;
|
||||
return video.play().catch(() => Promise.reject());
|
||||
};
|
||||
return tryPlay(false)
|
||||
.catch(() => tryPlay(true))
|
||||
.catch(() => {
|
||||
console.warn("[HLS] 自动播放被阻止,请点击画面播放");
|
||||
});
|
||||
},
|
||||
async initH5HlsPlayer(useHttpFallback = false) {
|
||||
const originalUrl = this.videoUrl;
|
||||
const container = this.$refs.hlsContainer;
|
||||
if (!originalUrl || !container) return;
|
||||
|
||||
const url = this.buildHlsPlayUrl(originalUrl, useHttpFallback);
|
||||
this.destroyH5HlsPlayer();
|
||||
|
||||
const video = document.createElement("video");
|
||||
video.style.cssText = "width:100%;height:100%;object-fit:cover;background:#000;";
|
||||
video.autoplay = true;
|
||||
video.muted = true;
|
||||
video.defaultMuted = true;
|
||||
video.playsInline = true;
|
||||
video.setAttribute("autoplay", "");
|
||||
video.setAttribute("muted", "");
|
||||
video.setAttribute("playsinline", "");
|
||||
video.setAttribute("webkit-playsinline", "");
|
||||
video.controls = false;
|
||||
|
||||
const onVideoClick = () => {
|
||||
if (video.muted) {
|
||||
video.muted = false;
|
||||
}
|
||||
video.play().catch(() => {});
|
||||
};
|
||||
video.addEventListener("click", onVideoClick);
|
||||
container.appendChild(video);
|
||||
this.hlsVideoElement = video;
|
||||
this._hlsVideoClickHandler = onVideoClick;
|
||||
|
||||
const onReadyPlay = () => {
|
||||
this.ensureH5VideoPlaying(video);
|
||||
};
|
||||
|
||||
let Hls = null;
|
||||
try {
|
||||
const hlsModule = await import("hls.js");
|
||||
Hls = hlsModule.default;
|
||||
} catch (error) {
|
||||
console.error("[HLS] 加载播放器失败:", error);
|
||||
this.$Message.error("直播播放器加载失败,请刷新页面重试");
|
||||
return;
|
||||
break;
|
||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||
console.error("[HLS] 媒体错误,尝试恢复...");
|
||||
hls.recoverMediaError();
|
||||
break;
|
||||
default:
|
||||
console.error("[HLS] 致命错误:", data);
|
||||
destroyH5HlsPlayer();
|
||||
Message.error("直播加载失败");
|
||||
break;
|
||||
}
|
||||
|
||||
if (Hls.isSupported()) {
|
||||
const hls = new Hls({
|
||||
enableWorker: true,
|
||||
lowLatencyMode: true,
|
||||
liveSyncDurationCount: 3,
|
||||
liveMaxLatencyDurationCount: 6,
|
||||
maxBufferLength: 10,
|
||||
maxMaxBufferLength: 30,
|
||||
});
|
||||
this.hlsInstance = hls;
|
||||
hls.on(Hls.Events.MEDIA_ATTACHED, onReadyPlay);
|
||||
hls.on(Hls.Events.MANIFEST_PARSED, onReadyPlay);
|
||||
hls.attachMedia(video);
|
||||
hls.loadSource(url);
|
||||
hls.on(Hls.Events.ERROR, (_event, data) => {
|
||||
if (!data.fatal) return;
|
||||
switch (data.type) {
|
||||
case Hls.ErrorTypes.NETWORK_ERROR:
|
||||
if (!this.hlsHttpFallbackUsed && originalUrl.startsWith("https://")) {
|
||||
console.warn("[HLS] HTTPS 加载失败,尝试 HTTP 降级...");
|
||||
this.hlsHttpFallbackUsed = true;
|
||||
this.destroyH5HlsPlayer();
|
||||
this.$nextTick(() => this.initH5HlsPlayer(true));
|
||||
} else {
|
||||
console.error("[HLS] 网络错误,尝试重连...");
|
||||
hls.startLoad();
|
||||
}
|
||||
break;
|
||||
case Hls.ErrorTypes.MEDIA_ERROR:
|
||||
console.error("[HLS] 媒体错误,尝试恢复...");
|
||||
hls.recoverMediaError();
|
||||
break;
|
||||
default:
|
||||
console.error("[HLS] 致命错误:", data);
|
||||
this.destroyH5HlsPlayer();
|
||||
this.$Message.error("直播加载失败");
|
||||
break;
|
||||
}
|
||||
});
|
||||
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
||||
video.src = url;
|
||||
video.addEventListener("loadedmetadata", onReadyPlay);
|
||||
video.addEventListener("canplay", onReadyPlay);
|
||||
} else {
|
||||
this.$Message.error("当前浏览器不支持 HLS 播放");
|
||||
});
|
||||
} else if (video.canPlayType("application/vnd.apple.mpegurl")) {
|
||||
video.src = url;
|
||||
video.addEventListener("loadedmetadata", onReadyPlay);
|
||||
video.addEventListener("canplay", onReadyPlay);
|
||||
} else {
|
||||
Message.error("当前浏览器不支持 HLS 播放");
|
||||
}
|
||||
}
|
||||
function destroyH5HlsPlayer() {
|
||||
if (hlsInstance) {
|
||||
hlsInstance.destroy();
|
||||
hlsInstance = null;
|
||||
}
|
||||
if (hlsVideoElement) {
|
||||
if (_hlsVideoClickHandler) {
|
||||
hlsVideoElement.removeEventListener("click", _hlsVideoClickHandler);
|
||||
_hlsVideoClickHandler = null;
|
||||
}
|
||||
hlsVideoElement.remove();
|
||||
hlsVideoElement = null;
|
||||
}
|
||||
const container = hlsContainer.value;
|
||||
if (container) {
|
||||
container.innerHTML = "";
|
||||
}
|
||||
}
|
||||
function loadDetail(showLoading = true) {
|
||||
if (showLoading) loading.value = true;
|
||||
getLiveInfo(liveId.value)
|
||||
.then((res) => {
|
||||
if (showLoading) loading.value = false;
|
||||
if (res.success) {
|
||||
const data = res.result || {};
|
||||
liveDetail.value = data;
|
||||
videoUrl.value = data.pullStreamUrl || data.streamUrl || "";
|
||||
syncH5Player();
|
||||
}
|
||||
},
|
||||
destroyH5HlsPlayer() {
|
||||
if (this.hlsInstance) {
|
||||
this.hlsInstance.destroy();
|
||||
this.hlsInstance = null;
|
||||
})
|
||||
.catch(() => {
|
||||
if (showLoading) loading.value = false;
|
||||
});
|
||||
}
|
||||
function loadOrderStats() {
|
||||
getLiveOrderStatistics(liveId.value).then((res) => {
|
||||
if (res.success) {
|
||||
orderStats.value = res.result || {};
|
||||
}
|
||||
});
|
||||
}
|
||||
function loadOnlineUsers() {
|
||||
onlineLoading.value = true;
|
||||
liveUserList({
|
||||
liveRoomId: liveId.value,
|
||||
pageNumber: onlinePagination.pageNumber,
|
||||
pageSize: onlinePagination.pageSize,
|
||||
})
|
||||
.then((res) => {
|
||||
onlineLoading.value = false;
|
||||
if (res.success) {
|
||||
onlineUserList.value = res.result?.records || [];
|
||||
onlinePagination.total = res.result?.total || 0;
|
||||
}
|
||||
if (this.hlsVideoElement) {
|
||||
if (this._hlsVideoClickHandler) {
|
||||
this.hlsVideoElement.removeEventListener("click", this._hlsVideoClickHandler);
|
||||
this._hlsVideoClickHandler = null;
|
||||
}
|
||||
this.hlsVideoElement.remove();
|
||||
this.hlsVideoElement = null;
|
||||
}
|
||||
const container = this.$refs.hlsContainer;
|
||||
if (container) {
|
||||
container.innerHTML = "";
|
||||
}
|
||||
},
|
||||
loadDetail(showLoading = true) {
|
||||
if (showLoading) this.loading = true;
|
||||
getLiveInfo(this.liveId)
|
||||
.then((res) => {
|
||||
if (showLoading) this.loading = false;
|
||||
if (res.success) {
|
||||
const data = res.result || {};
|
||||
this.liveDetail = data;
|
||||
this.videoUrl = data.pullStreamUrl || data.streamUrl || "";
|
||||
this.syncH5Player();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (showLoading) this.loading = false;
|
||||
});
|
||||
},
|
||||
loadOrderStats() {
|
||||
getLiveOrderStatistics(this.liveId).then((res) => {
|
||||
})
|
||||
.catch(() => {
|
||||
onlineLoading.value = false;
|
||||
});
|
||||
}
|
||||
function buildShareUrl() {
|
||||
const base = (wapUrl.value || BASE?.WAP_URL || config.website || "").replace(/\/?$/, "/");
|
||||
return `${base}pages/promotion/live/room?id=${liveId.value}`;
|
||||
}
|
||||
function handleShare() {
|
||||
const url = buildShareUrl();
|
||||
if (!url) {
|
||||
Message.warning("未配置移动端地址");
|
||||
return;
|
||||
}
|
||||
copyText(url, "分享链接");
|
||||
}
|
||||
function copyText(text, label = "内容") {
|
||||
if (!text) {
|
||||
Message.warning(`${label}为空,无法复制`);
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(text);
|
||||
Message.success(`${label}已复制`);
|
||||
}
|
||||
function handleStart() {
|
||||
Modal.confirm({
|
||||
title: "确认开播",
|
||||
content: "确定要开始该直播吗?",
|
||||
onOk: () => {
|
||||
return startLive(liveId.value).then((res) => {
|
||||
if (res.success) {
|
||||
this.orderStats = res.result || {};
|
||||
Message.success("开播成功");
|
||||
refreshAll();
|
||||
}
|
||||
});
|
||||
},
|
||||
loadOnlineUsers() {
|
||||
this.onlineLoading = true;
|
||||
liveUserList({
|
||||
liveRoomId: this.liveId,
|
||||
pageNumber: this.onlinePagination.pageNumber,
|
||||
pageSize: this.onlinePagination.pageSize,
|
||||
})
|
||||
.then((res) => {
|
||||
this.onlineLoading = false;
|
||||
if (res.success) {
|
||||
this.onlineUserList = res.result?.records || [];
|
||||
this.onlinePagination.total = res.result?.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.onlineLoading = false;
|
||||
});
|
||||
},
|
||||
buildShareUrl() {
|
||||
const base = (this.wapUrl || BASE?.WAP_URL || config.website || "").replace(/\/?$/, "/");
|
||||
return `${base}pages/promotion/live/room?id=${this.liveId}`;
|
||||
},
|
||||
handleShare() {
|
||||
const url = this.buildShareUrl();
|
||||
if (!url) {
|
||||
this.$Message.warning("未配置移动端地址");
|
||||
return;
|
||||
}
|
||||
this.copyText(url, "分享链接");
|
||||
},
|
||||
copyText(text, label = "内容") {
|
||||
if (!text) {
|
||||
this.$Message.warning(`${label}为空,无法复制`);
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(text);
|
||||
this.$Message.success(`${label}已复制`);
|
||||
},
|
||||
handleStart() {
|
||||
this.$Modal.confirm({
|
||||
title: "确认开播",
|
||||
content: "确定要开始该直播吗?",
|
||||
onOk: () => {
|
||||
return startLive(this.liveId).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("开播成功");
|
||||
this.refreshAll();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
function handleEnd() {
|
||||
Modal.confirm({
|
||||
title: "确认结束",
|
||||
content: "确定要结束该直播吗?",
|
||||
onOk: () => {
|
||||
return endLive(liveId.value).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("直播已结束");
|
||||
destroyH5HlsPlayer();
|
||||
stopPolling();
|
||||
refreshAll();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleEnd() {
|
||||
this.$Modal.confirm({
|
||||
title: "确认结束",
|
||||
content: "确定要结束该直播吗?",
|
||||
onOk: () => {
|
||||
return endLive(this.liveId).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("直播已结束");
|
||||
this.destroyH5HlsPlayer();
|
||||
this.stopPolling();
|
||||
this.refreshAll();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -42,66 +42,66 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { getLiveInfo } from "@/api/live";
|
||||
import { Message } from "@/utils/message";
|
||||
import { unixToDate } from "@/utils/filters";
|
||||
import LiveGoods from "./components/LiveGoods.vue";
|
||||
|
||||
export default {
|
||||
name: "live-detail",
|
||||
components: { LiveGoods },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
liveId: "",
|
||||
detail: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
liveStatus() {
|
||||
return this.detail.liveStatus || this.detail.status || "";
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.liveId = String(this.$route.query.id || "");
|
||||
if (!this.liveId) {
|
||||
this.$Message.error("缺少直播ID");
|
||||
this.handleBack();
|
||||
return;
|
||||
}
|
||||
this.loadDetail();
|
||||
},
|
||||
methods: {
|
||||
formatTime(time) {
|
||||
if (!time) return "-";
|
||||
return this.$filters.unixToDate(time);
|
||||
},
|
||||
liveStatusText(status) {
|
||||
const map = { NEW: "未开始", LIVING: "直播中", ENDED: "已结束" };
|
||||
return map[status] || "未知";
|
||||
},
|
||||
liveStatusTagType(status) {
|
||||
const map = { NEW: "info", LIVING: "success", ENDED: "warning" };
|
||||
return map[status] || "info";
|
||||
},
|
||||
loadDetail() {
|
||||
this.loading = true;
|
||||
getLiveInfo(this.liveId)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.detail = res.result || {};
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
this.handleBack();
|
||||
});
|
||||
},
|
||||
handleBack() {
|
||||
this.$router.push({ path: "/live-list" });
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "live-detail" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const loading = ref(false);
|
||||
const liveId = ref("");
|
||||
const detail = ref({});
|
||||
|
||||
const liveStatus = computed(() => {
|
||||
return detail.value.liveStatus || detail.value.status || "";
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
liveId.value = String(route.query.id || "");
|
||||
if (!liveId.value) {
|
||||
Message.error("缺少直播ID");
|
||||
handleBack();
|
||||
return;
|
||||
}
|
||||
loadDetail();
|
||||
});
|
||||
|
||||
function formatTime(time) {
|
||||
if (!time) return "-";
|
||||
return unixToDate(time);
|
||||
}
|
||||
function liveStatusText(status) {
|
||||
const map = { NEW: "未开始", LIVING: "直播中", ENDED: "已结束" };
|
||||
return map[status] || "未知";
|
||||
}
|
||||
function liveStatusTagType(status) {
|
||||
const map = { NEW: "info", LIVING: "success", ENDED: "warning" };
|
||||
return map[status] || "info";
|
||||
}
|
||||
function loadDetail() {
|
||||
loading.value = true;
|
||||
getLiveInfo(liveId.value)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
detail.value = res.result || {};
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
handleBack();
|
||||
});
|
||||
}
|
||||
function handleBack() {
|
||||
router.push({ path: "/live-list" });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<template #header>
|
||||
<span class="page-title">编辑直播</span>
|
||||
</template>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="100px" class="live-form">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px" class="live-form">
|
||||
<el-form-item v-if="form.liveStatus !== 'NEW'" label="直播状态">
|
||||
<el-tag :type="liveStatusTagType(form.liveStatus)">{{ liveStatusText(form.liveStatus) }}</el-tag>
|
||||
</el-form-item>
|
||||
@@ -81,7 +81,7 @@
|
||||
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
@callback="callbackSelected"
|
||||
@@ -90,11 +90,22 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { nextTick, onMounted, reactive, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { Picture } from "@element-plus/icons-vue";
|
||||
import { getLiveInfo, updateLive } from "@/api/live";
|
||||
import { Message } from "@/utils/message";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
|
||||
defineOptions({ name: "live-edit" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const formRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function getCharLength(str) {
|
||||
if (!str) return 0;
|
||||
let len = 0;
|
||||
@@ -130,133 +141,126 @@ function formatDateTime(value) {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "live-edit",
|
||||
components: { Picture, ossManage },
|
||||
data() {
|
||||
const validateTitle = (rule, value, callback) => {
|
||||
const len = getCharLength(value);
|
||||
if (len < 6) {
|
||||
callback(new Error("直播间名字最短3个汉字"));
|
||||
return;
|
||||
}
|
||||
if (len > 34) {
|
||||
callback(new Error("直播间名字最长17个汉字"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
return {
|
||||
picModelFlag: false,
|
||||
loading: false,
|
||||
submitting: false,
|
||||
liveId: "",
|
||||
endTime: "",
|
||||
form: {
|
||||
title: "",
|
||||
displayMode: "0",
|
||||
startTime: "",
|
||||
coverImg: "",
|
||||
liveIntroduce: "",
|
||||
liveStatus: "NEW",
|
||||
},
|
||||
rules: {
|
||||
title: [
|
||||
{ required: true, message: "请输入直播标题", trigger: "blur" },
|
||||
{ validator: validateTitle, trigger: "blur" },
|
||||
],
|
||||
displayMode: [{ required: true, message: "请选择显示模式", trigger: "change" }],
|
||||
startTime: [{ required: true, message: "请选择直播开始时间", trigger: "change" }],
|
||||
coverImg: [{ required: true, message: "请上传直播封面", trigger: "change" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.liveId = String(this.$route.query.id || "");
|
||||
if (!this.liveId) {
|
||||
this.$Message.error("缺少直播ID");
|
||||
this.handleBack();
|
||||
return;
|
||||
}
|
||||
this.loadDetail();
|
||||
},
|
||||
methods: {
|
||||
liveStatusText(status) {
|
||||
const map = { NEW: "未开始", LIVING: "直播中", ENDED: "已结束" };
|
||||
return map[status] || "未知";
|
||||
},
|
||||
liveStatusTagType(status) {
|
||||
const map = { NEW: "info", LIVING: "success", ENDED: "warning" };
|
||||
return map[status] || "info";
|
||||
},
|
||||
openCoverPicker() {
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (val?.url) {
|
||||
this.form.coverImg = val.url;
|
||||
this.$refs.form?.validateField("coverImg");
|
||||
}
|
||||
},
|
||||
loadDetail() {
|
||||
this.loading = true;
|
||||
getLiveInfo(this.liveId)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
const data = res.result || {};
|
||||
this.form.title = data.title || "";
|
||||
this.form.displayMode = normalizeDisplayMode(data.displayMode);
|
||||
this.form.coverImg = data.coverImg || "";
|
||||
this.form.liveIntroduce = data.liveIntroduce || "";
|
||||
this.form.liveStatus = data.liveStatus || data.status || "NEW";
|
||||
this.endTime = formatDateTime(data.endTime);
|
||||
this.form.startTime = formatDateTime(data.startTime);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return;
|
||||
let endTime = this.endTime;
|
||||
if (!endTime || endTime <= this.form.startTime) {
|
||||
endTime = buildEndTime(this.form.startTime);
|
||||
}
|
||||
this.submitting = true;
|
||||
updateLive(this.liveId, {
|
||||
title: this.form.title,
|
||||
displayMode: this.form.displayMode,
|
||||
coverImg: this.form.coverImg,
|
||||
startTime: this.form.startTime,
|
||||
endTime,
|
||||
liveIntroduce: this.form.liveIntroduce,
|
||||
})
|
||||
.then((res) => {
|
||||
this.submitting = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("保存成功");
|
||||
this.$router.push({ path: "/live-list" });
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.submitting = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
handleBack() {
|
||||
this.$router.push({ path: "/live-list" });
|
||||
},
|
||||
},
|
||||
const validateTitle = (rule, value, callback) => {
|
||||
const len = getCharLength(value);
|
||||
if (len < 6) {
|
||||
callback(new Error("直播间名字最短3个汉字"));
|
||||
return;
|
||||
}
|
||||
if (len > 34) {
|
||||
callback(new Error("直播间名字最长17个汉字"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
const picModelFlag = ref(false);
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const liveId = ref("");
|
||||
const endTime = ref("");
|
||||
const form = reactive({
|
||||
title: "",
|
||||
displayMode: "0",
|
||||
startTime: "",
|
||||
coverImg: "",
|
||||
liveIntroduce: "",
|
||||
liveStatus: "NEW",
|
||||
});
|
||||
const rules = reactive({
|
||||
title: [
|
||||
{ required: true, message: "请输入直播标题", trigger: "blur" },
|
||||
{ validator: validateTitle, trigger: "blur" },
|
||||
],
|
||||
displayMode: [{ required: true, message: "请选择显示模式", trigger: "change" }],
|
||||
startTime: [{ required: true, message: "请选择直播开始时间", trigger: "change" }],
|
||||
coverImg: [{ required: true, message: "请上传直播封面", trigger: "change" }],
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
liveId.value = String(route.query.id || "");
|
||||
if (!liveId.value) {
|
||||
Message.error("缺少直播ID");
|
||||
handleBack();
|
||||
return;
|
||||
}
|
||||
loadDetail();
|
||||
});
|
||||
|
||||
function liveStatusText(status) {
|
||||
const map = { NEW: "未开始", LIVING: "直播中", ENDED: "已结束" };
|
||||
return map[status] || "未知";
|
||||
}
|
||||
function liveStatusTagType(status) {
|
||||
const map = { NEW: "info", LIVING: "success", ENDED: "warning" };
|
||||
return map[status] || "info";
|
||||
}
|
||||
function openCoverPicker() {
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
if (val?.url) {
|
||||
form.coverImg = val.url;
|
||||
formRef.value?.validateField("coverImg");
|
||||
}
|
||||
}
|
||||
function loadDetail() {
|
||||
loading.value = true;
|
||||
getLiveInfo(liveId.value)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
const data = res.result || {};
|
||||
form.title = data.title || "";
|
||||
form.displayMode = normalizeDisplayMode(data.displayMode);
|
||||
form.coverImg = data.coverImg || "";
|
||||
form.liveIntroduce = data.liveIntroduce || "";
|
||||
form.liveStatus = data.liveStatus || data.status || "NEW";
|
||||
endTime.value = formatDateTime(data.endTime);
|
||||
form.startTime = formatDateTime(data.startTime);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function handleSubmit() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
let endTimeVal = endTime.value;
|
||||
if (!endTimeVal || endTimeVal <= form.startTime) {
|
||||
endTimeVal = buildEndTime(form.startTime);
|
||||
}
|
||||
submitting.value = true;
|
||||
updateLive(liveId.value, {
|
||||
title: form.title,
|
||||
displayMode: form.displayMode,
|
||||
coverImg: form.coverImg,
|
||||
startTime: form.startTime,
|
||||
endTime: endTimeVal,
|
||||
liveIntroduce: form.liveIntroduce,
|
||||
})
|
||||
.then((res) => {
|
||||
submitting.value = false;
|
||||
if (res.success) {
|
||||
Message.success("保存成功");
|
||||
router.push({ path: "/live-list" });
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
submitting.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
function handleBack() {
|
||||
router.push({ path: "/live-list" });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="live-list search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="90px"
|
||||
@@ -135,202 +135,206 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { queryLivePage, startLive, endLive } from "@/api/live";
|
||||
import { getSetting } from "@/api/index";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
import { unixToDate } from "@/utils/filters";
|
||||
import config from "@/config";
|
||||
|
||||
export default {
|
||||
name: "live-list",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
activeStatus: "",
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
title: "",
|
||||
storeName: "",
|
||||
},
|
||||
shareVisible: false,
|
||||
shareTitle: "",
|
||||
shareH5Url: "",
|
||||
wapUrl: "",
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loadWapSetting();
|
||||
this.getData();
|
||||
},
|
||||
methods: {
|
||||
formatTime(time) {
|
||||
if (!time) return "-";
|
||||
return this.$filters.unixToDate(time);
|
||||
},
|
||||
liveStatusText(status) {
|
||||
const map = { NEW: "未开始", LIVING: "直播中", ENDED: "已结束" };
|
||||
return map[status] || "未知";
|
||||
},
|
||||
liveStatusTagType(status) {
|
||||
const map = { NEW: "info", LIVING: "success", ENDED: "warning" };
|
||||
return map[status] || "info";
|
||||
},
|
||||
buildParams() {
|
||||
const params = {
|
||||
pageNumber: this.searchForm.pageNumber,
|
||||
pageSize: this.searchForm.pageSize,
|
||||
};
|
||||
if (this.searchForm.title) params.title = this.searchForm.title;
|
||||
if (this.searchForm.storeName) params.storeName = this.searchForm.storeName;
|
||||
if (this.activeStatus) params.liveStatus = this.activeStatus;
|
||||
return params;
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
queryLivePage(this.buildParams())
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result?.records || [];
|
||||
this.total = res.result?.total || 0;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getData();
|
||||
},
|
||||
handleReset() {
|
||||
this.searchForm.title = "";
|
||||
this.searchForm.storeName = "";
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getData();
|
||||
},
|
||||
handleTabChange() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getData();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getData();
|
||||
},
|
||||
handleAdd() {
|
||||
this.$router.push({ path: "/live-add" });
|
||||
},
|
||||
handleView(row) {
|
||||
this.$router.push({ path: "/live-manage-detail", query: { id: row.id } });
|
||||
},
|
||||
handleEdit(row) {
|
||||
this.$router.push({ path: "/live-edit", query: { id: row.id } });
|
||||
},
|
||||
handleControl(row) {
|
||||
const route = this.$router.resolve({
|
||||
path: "/live-control-panel",
|
||||
query: { id: row.id },
|
||||
});
|
||||
window.open(route.href, "_blank");
|
||||
},
|
||||
buildH5ShareUrl(liveId) {
|
||||
const base = (this.wapUrl || BASE?.WAP_URL || config.website || "").replace(/\/?$/, "/");
|
||||
return `${base}pages/promotion/live/room?id=${liveId}`;
|
||||
},
|
||||
handleShare(row) {
|
||||
this.shareTitle = row.title || "";
|
||||
this.shareH5Url = this.buildH5ShareUrl(row.id);
|
||||
this.shareVisible = true;
|
||||
},
|
||||
copyShareUrl() {
|
||||
if (!this.shareH5Url) return;
|
||||
this.copyText(this.shareH5Url);
|
||||
},
|
||||
copyText(text) {
|
||||
const onSuccess = () => this.$Message.success("链接已复制");
|
||||
const onFail = () => this.$Message.error("复制失败,请手动复制");
|
||||
defineOptions({ name: "live-list" });
|
||||
|
||||
if (navigator.clipboard?.writeText) {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(onSuccess)
|
||||
.catch(() => {
|
||||
this.copyTextFallback(text) ? onSuccess() : onFail();
|
||||
});
|
||||
return;
|
||||
}
|
||||
const router = useRouter();
|
||||
|
||||
this.copyTextFallback(text) ? onSuccess() : onFail();
|
||||
},
|
||||
copyTextFallback(text) {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
textArea.style.position = "fixed";
|
||||
textArea.style.left = "-9999px";
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
let ok = false;
|
||||
try {
|
||||
ok = document.execCommand("copy");
|
||||
} catch (e) {
|
||||
ok = false;
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const activeStatus = ref("");
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
title: "",
|
||||
storeName: "",
|
||||
});
|
||||
const shareVisible = ref(false);
|
||||
const shareTitle = ref("");
|
||||
const shareH5Url = ref("");
|
||||
const wapUrl = ref("");
|
||||
|
||||
onMounted(() => {
|
||||
loadWapSetting();
|
||||
getData();
|
||||
});
|
||||
|
||||
function formatTime(time) {
|
||||
if (!time) return "-";
|
||||
return unixToDate(time);
|
||||
}
|
||||
function liveStatusText(status) {
|
||||
const map = { NEW: "未开始", LIVING: "直播中", ENDED: "已结束" };
|
||||
return map[status] || "未知";
|
||||
}
|
||||
function liveStatusTagType(status) {
|
||||
const map = { NEW: "info", LIVING: "success", ENDED: "warning" };
|
||||
return map[status] || "info";
|
||||
}
|
||||
function buildParams() {
|
||||
const params = {
|
||||
pageNumber: searchForm.pageNumber,
|
||||
pageSize: searchForm.pageSize,
|
||||
};
|
||||
if (searchForm.title) params.title = searchForm.title;
|
||||
if (searchForm.storeName) params.storeName = searchForm.storeName;
|
||||
if (activeStatus.value) params.liveStatus = activeStatus.value;
|
||||
return params;
|
||||
}
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
queryLivePage(buildParams())
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result?.records || [];
|
||||
total.value = res.result?.total || 0;
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
return ok;
|
||||
},
|
||||
handleStart(row) {
|
||||
if (row.liveStatus === "LIVING") {
|
||||
this.$Message.warning("直播间已在直播中");
|
||||
return;
|
||||
}
|
||||
if (row.liveStatus === "ENDED") {
|
||||
this.$Message.warning("直播已结束,无法开播");
|
||||
return;
|
||||
}
|
||||
this.$Modal.confirm({
|
||||
title: "确认开播",
|
||||
content: "确定要开始该直播吗?",
|
||||
onOk: () => {
|
||||
return startLive(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("开播成功");
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
function handleReset() {
|
||||
searchForm.title = "";
|
||||
searchForm.storeName = "";
|
||||
searchForm.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
function handleTabChange() {
|
||||
searchForm.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
function handleAdd() {
|
||||
router.push({ path: "/live-add" });
|
||||
}
|
||||
function handleView(row) {
|
||||
router.push({ path: "/live-manage-detail", query: { id: row.id } });
|
||||
}
|
||||
function handleEdit(row) {
|
||||
router.push({ path: "/live-edit", query: { id: row.id } });
|
||||
}
|
||||
function handleControl(row) {
|
||||
const route = router.resolve({
|
||||
path: "/live-control-panel",
|
||||
query: { id: row.id },
|
||||
});
|
||||
window.open(route.href, "_blank");
|
||||
}
|
||||
function buildH5ShareUrl(liveId) {
|
||||
// 历史遗留:BASE 未定义,wapUrl 为空时会抛 ReferenceError,保持原样
|
||||
const base = (wapUrl.value || BASE?.WAP_URL || config.website || "").replace(/\/?$/, "/");
|
||||
return `${base}pages/promotion/live/room?id=${liveId}`;
|
||||
}
|
||||
function handleShare(row) {
|
||||
shareTitle.value = row.title || "";
|
||||
shareH5Url.value = buildH5ShareUrl(row.id);
|
||||
shareVisible.value = true;
|
||||
}
|
||||
function copyShareUrl() {
|
||||
if (!shareH5Url.value) return;
|
||||
copyText(shareH5Url.value);
|
||||
}
|
||||
function copyText(text) {
|
||||
const onSuccess = () => Message.success("链接已复制");
|
||||
const onFail = () => Message.error("复制失败,请手动复制");
|
||||
|
||||
if (navigator.clipboard?.writeText) {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(onSuccess)
|
||||
.catch(() => {
|
||||
copyTextFallback(text) ? onSuccess() : onFail();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
copyTextFallback(text) ? onSuccess() : onFail();
|
||||
}
|
||||
function copyTextFallback(text) {
|
||||
const textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
textArea.style.position = "fixed";
|
||||
textArea.style.left = "-9999px";
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
let ok = false;
|
||||
try {
|
||||
ok = document.execCommand("copy");
|
||||
} catch (e) {
|
||||
ok = false;
|
||||
}
|
||||
document.body.removeChild(textArea);
|
||||
return ok;
|
||||
}
|
||||
function handleStart(row) {
|
||||
if (row.liveStatus === "LIVING") {
|
||||
Message.warning("直播间已在直播中");
|
||||
return;
|
||||
}
|
||||
if (row.liveStatus === "ENDED") {
|
||||
Message.warning("直播已结束,无法开播");
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: "确认开播",
|
||||
content: "确定要开始该直播吗?",
|
||||
onOk: () => {
|
||||
return startLive(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("开播成功");
|
||||
getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
handleEnd(row) {
|
||||
if (row.liveStatus !== "LIVING") {
|
||||
this.$Message.warning("仅直播中的直播间可以结束");
|
||||
return;
|
||||
}
|
||||
this.$Modal.confirm({
|
||||
title: "确认结束",
|
||||
content: "确定要结束该直播吗?",
|
||||
onOk: () => {
|
||||
return endLive(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("直播已结束");
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
function handleEnd(row) {
|
||||
if (row.liveStatus !== "LIVING") {
|
||||
Message.warning("仅直播中的直播间可以结束");
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: "确认结束",
|
||||
content: "确定要结束该直播吗?",
|
||||
onOk: () => {
|
||||
return endLive(row.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("直播已结束");
|
||||
getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
loadWapSetting() {
|
||||
getSetting("BASE_SETTING")
|
||||
.then((res) => {
|
||||
if (res.success && res.result?.buyerDomain) {
|
||||
this.wapUrl = res.result.buyerDomain;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
function loadWapSetting() {
|
||||
getSetting("BASE_SETTING")
|
||||
.then((res) => {
|
||||
if (res.success && res.result?.buyerDomain) {
|
||||
wapUrl.value = res.result.buyerDomain;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="login" @click="$refs.verify.show = false">
|
||||
<div class="login" @click="hideVerify">
|
||||
<div class="login-brand">
|
||||
<div class="brand-inner">
|
||||
<p class="brand-name">LILISHOP</p>
|
||||
@@ -24,7 +24,7 @@
|
||||
</div>
|
||||
|
||||
<el-form
|
||||
ref="usernameLoginForm"
|
||||
ref="usernameLoginFormRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
class="form"
|
||||
@@ -70,7 +70,7 @@
|
||||
</el-button>
|
||||
|
||||
<verify
|
||||
ref="verify"
|
||||
ref="verifyRef"
|
||||
class="verify-con"
|
||||
verifyType="LOGIN"
|
||||
@change="verifyChange"
|
||||
@@ -81,7 +81,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { User, Lock } from "@element-plus/icons-vue";
|
||||
import { login, userInfo } from "@/api/index";
|
||||
import Cookies from "js-cookie";
|
||||
@@ -89,82 +91,79 @@ import Header from "@/views/main-parts/header";
|
||||
import Footer from "@/views/main-parts/footer";
|
||||
import util from "@/libs/util.js";
|
||||
import verify from "@/components/verify";
|
||||
import { setStore } from "@/libs/storage";
|
||||
import { md5 } from "@/utils/md5.js";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Header,
|
||||
Footer,
|
||||
verify,
|
||||
User,
|
||||
Lock,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
year: new Date().getFullYear(),
|
||||
loading: false,
|
||||
form: {
|
||||
username: "",
|
||||
password: "",
|
||||
mobile: "",
|
||||
code: "",
|
||||
},
|
||||
rules: {
|
||||
username: [
|
||||
{ required: true, message: "账号不能为空", trigger: "blur" },
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: "密码不能为空", trigger: "blur" },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
afterLogin(res) {
|
||||
const accessToken = res.result.accessToken;
|
||||
const refreshToken = res.result.refreshToken;
|
||||
this.setStore("accessToken", accessToken);
|
||||
this.setStore("refreshToken", refreshToken);
|
||||
userInfo().then((res) => {
|
||||
if (res.success) {
|
||||
Cookies.set("userInfoManager", JSON.stringify(res.result));
|
||||
this.$store.commit("setAvatarPath", res.result.avatar);
|
||||
util.initRouter(this);
|
||||
this.$store.commit("setOpenedList");
|
||||
this.$store.commit("initCachePage");
|
||||
this.$router.push({ name: "home_index" });
|
||||
} else {
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
submitLogin() {
|
||||
this.$refs.usernameLoginForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$refs.verify.init();
|
||||
}
|
||||
});
|
||||
},
|
||||
verifyChange(con) {
|
||||
if (!con.status) return;
|
||||
this.loading = true;
|
||||
const fd = new FormData();
|
||||
fd.append("username", this.form.username);
|
||||
fd.append("password", this.md5(this.form.password));
|
||||
login(fd)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
this.afterLogin(res);
|
||||
} else {
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
this.$refs.verify.show = false;
|
||||
},
|
||||
},
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const year = ref(new Date().getFullYear());
|
||||
const loading = ref(false);
|
||||
const form = reactive({
|
||||
username: "",
|
||||
password: "",
|
||||
mobile: "",
|
||||
code: "",
|
||||
});
|
||||
const rules = {
|
||||
username: [{ required: true, message: "账号不能为空", trigger: "blur" }],
|
||||
password: [{ required: true, message: "密码不能为空", trigger: "blur" }],
|
||||
};
|
||||
|
||||
const usernameLoginFormRef = ref(null);
|
||||
const verifyRef = ref(null);
|
||||
|
||||
function hideVerify() {
|
||||
verifyRef.value.show = false;
|
||||
}
|
||||
|
||||
function afterLogin(res) {
|
||||
const accessToken = res.result.accessToken;
|
||||
const refreshToken = res.result.refreshToken;
|
||||
setStore("accessToken", accessToken);
|
||||
setStore("refreshToken", refreshToken);
|
||||
userInfo().then((res) => {
|
||||
if (res.success) {
|
||||
Cookies.set("userInfoManager", JSON.stringify(res.result));
|
||||
appStore.setAvatarPath(res.result.avatar);
|
||||
util.initRouter();
|
||||
appStore.setOpenedList();
|
||||
appStore.initCachePage();
|
||||
router.push({ name: "home_index" });
|
||||
} else {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function submitLogin() {
|
||||
usernameLoginFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
verifyRef.value.init();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function verifyChange(con) {
|
||||
if (!con.status) return;
|
||||
loading.value = true;
|
||||
const fd = new FormData();
|
||||
fd.append("username", form.username);
|
||||
fd.append("password", md5(form.password));
|
||||
login(fd)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
afterLogin(res);
|
||||
} else {
|
||||
loading.value = false;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
verifyRef.value.show = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form ref="form" :model="form" :rules="formValidate" label-width="120px">
|
||||
<el-form ref="formRef" :model="form" :rules="formValidate" label-width="120px">
|
||||
<el-form-item label="物流公司名称" prop="name">
|
||||
<el-input v-model="form.name" clearable style="width: 100%" />
|
||||
</el-form-item>
|
||||
@@ -97,13 +97,17 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { nextTick, onMounted, reactive, ref } from "vue";
|
||||
import {
|
||||
getLogisticsPage,
|
||||
updateLogistics,
|
||||
addLogistics,
|
||||
delLogistics,
|
||||
} from "@/api/logistics";
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
|
||||
defineOptions({ name: "logistics" });
|
||||
|
||||
const defaultForm = () => ({
|
||||
name: "",
|
||||
@@ -114,156 +118,152 @@ const defaultForm = () => ({
|
||||
|
||||
const normalizeStandBy = (value) => (value === true || value === "true" ? "true" : "false");
|
||||
|
||||
export default {
|
||||
name: "logistics",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
modalVisible: false,
|
||||
modalTitle: "",
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
name: "",
|
||||
},
|
||||
form: defaultForm(),
|
||||
formValidate: {
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入物流公司名称",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入物流公司编码",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
},
|
||||
submitLoading: false,
|
||||
data: [],
|
||||
total: 0,
|
||||
id: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getLogisticsPage(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
const data = res.result.records;
|
||||
data.forEach((e) => {
|
||||
e.switch = e.disabled === "OPEN";
|
||||
e.standBy = normalizeStandBy(e.standBy);
|
||||
});
|
||||
this.data = data;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
changeSwitch(row) {
|
||||
updateLogistics(row.id, {
|
||||
name: row.name,
|
||||
code: row.code,
|
||||
standBy: normalizeStandBy(row.standBy),
|
||||
disabled: row.disabled === "CLOSE" ? "OPEN" : "CLOSE",
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
} else {
|
||||
row.switch = !row.switch;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitLoading = true;
|
||||
const table = ref(null);
|
||||
const formRef = ref(null);
|
||||
|
||||
if (this.modalTitle === "添加") {
|
||||
const payload = {
|
||||
name: this.form.name,
|
||||
code: this.form.code,
|
||||
disabled: this.form.disabled,
|
||||
standBy: normalizeStandBy(this.form.standBy),
|
||||
formItems: this.form.formItems,
|
||||
};
|
||||
addLogistics(payload).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateLogistics(this.id, {
|
||||
...this.form,
|
||||
standBy: normalizeStandBy(this.form.standBy),
|
||||
}).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
const loading = ref(true);
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref("");
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
name: "",
|
||||
});
|
||||
const form = ref(defaultForm());
|
||||
const formValidate = reactive({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入物流公司名称",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
code: [
|
||||
{
|
||||
required: true,
|
||||
message: "请输入物流公司编码",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
});
|
||||
const submitLoading = ref(false);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const id = ref("");
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getLogisticsPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
const records = res.result.records;
|
||||
records.forEach((e) => {
|
||||
e.switch = e.disabled === "OPEN";
|
||||
e.standBy = normalizeStandBy(e.standBy);
|
||||
});
|
||||
data.value = records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function changeSwitch(row) {
|
||||
updateLogistics(row.id, {
|
||||
name: row.name,
|
||||
code: row.code,
|
||||
standBy: normalizeStandBy(row.standBy),
|
||||
disabled: row.disabled === "CLOSE" ? "OPEN" : "CLOSE",
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
} else {
|
||||
row.switch = !row.switch;
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleSubmit() {
|
||||
formRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitLoading.value = true;
|
||||
|
||||
if (modalTitle.value === "添加") {
|
||||
const payload = {
|
||||
name: form.value.name,
|
||||
code: form.value.code,
|
||||
disabled: form.value.disabled,
|
||||
standBy: normalizeStandBy(form.value.standBy),
|
||||
formItems: form.value.formItems,
|
||||
};
|
||||
addLogistics(payload).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
updateLogistics(id.value, {
|
||||
...form.value,
|
||||
standBy: normalizeStandBy(form.value.standBy),
|
||||
}).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function add() {
|
||||
modalTitle.value = "添加";
|
||||
form.value = defaultForm();
|
||||
nextTick(() => {
|
||||
formRef.value?.resetFields();
|
||||
});
|
||||
modalVisible.value = true;
|
||||
}
|
||||
function detail(v) {
|
||||
id.value = v.id;
|
||||
modalTitle.value = "修改";
|
||||
form.value = {
|
||||
name: v.name,
|
||||
code: v.code,
|
||||
standBy: normalizeStandBy(v.standBy),
|
||||
disabled: v.disabled,
|
||||
};
|
||||
modalVisible.value = true;
|
||||
}
|
||||
function remove(v) {
|
||||
Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除 " + v.name + " ?",
|
||||
onOk: () => {
|
||||
return delLogistics(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("操作成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
add() {
|
||||
this.modalTitle = "添加";
|
||||
this.form = defaultForm();
|
||||
this.$nextTick(() => {
|
||||
this.$refs.form?.resetFields();
|
||||
});
|
||||
this.modalVisible = true;
|
||||
},
|
||||
detail(v) {
|
||||
this.id = v.id;
|
||||
this.modalTitle = "修改";
|
||||
this.form = {
|
||||
name: v.name,
|
||||
code: v.code,
|
||||
standBy: normalizeStandBy(v.standBy),
|
||||
disabled: v.disabled,
|
||||
};
|
||||
this.modalVisible = true;
|
||||
},
|
||||
remove(v) {
|
||||
this.$Modal.confirm({
|
||||
title: "确认删除",
|
||||
content: "您确认要删除 " + v.name + " ?",
|
||||
onOk: () => {
|
||||
return delLogistics(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("操作成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -12,17 +12,11 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import config from "@/config/index";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
config,
|
||||
year: new Date().getFullYear(),
|
||||
};
|
||||
},
|
||||
};
|
||||
const year = ref(new Date().getFullYear());
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -6,50 +6,46 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { getBaseSite } from "@/api/common.js";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
domainLogo: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
applyFavicon(href) {
|
||||
if (!href) return;
|
||||
let link =
|
||||
document.querySelector("link[rel*='icon']") ||
|
||||
document.createElement("link");
|
||||
link.type = "image/x-icon";
|
||||
link.href = href;
|
||||
link.rel = "shortcut icon";
|
||||
document.getElementsByTagName("head")[0].appendChild(link);
|
||||
},
|
||||
getSite() {
|
||||
getBaseSite().then((res) => {
|
||||
if (res.success && res.result && res.result.settingValue) {
|
||||
const { domainLogo, domainIcon, siteName } = JSON.parse(
|
||||
res.result.settingValue
|
||||
);
|
||||
this.domainLogo = domainLogo || "";
|
||||
const expirationTime = new Date().setHours(new Date().getHours() + 1);
|
||||
localStorage.setItem("icontitle_expiration_time", expirationTime);
|
||||
localStorage.setItem("icon", domainLogo || "");
|
||||
localStorage.setItem("domainIcon", domainIcon || "");
|
||||
localStorage.setItem("title", siteName || "");
|
||||
this.applyFavicon(domainIcon || domainLogo);
|
||||
if (siteName) {
|
||||
window.document.title = siteName + " - 运营后台";
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getSite();
|
||||
},
|
||||
};
|
||||
const domainLogo = ref("");
|
||||
|
||||
function applyFavicon(href) {
|
||||
if (!href) return;
|
||||
let link =
|
||||
document.querySelector("link[rel*='icon']") ||
|
||||
document.createElement("link");
|
||||
link.type = "image/x-icon";
|
||||
link.href = href;
|
||||
link.rel = "shortcut icon";
|
||||
document.getElementsByTagName("head")[0].appendChild(link);
|
||||
}
|
||||
|
||||
function getSite() {
|
||||
getBaseSite().then((res) => {
|
||||
if (res.success && res.result && res.result.settingValue) {
|
||||
const { domainLogo: logo, domainIcon, siteName } = JSON.parse(
|
||||
res.result.settingValue
|
||||
);
|
||||
domainLogo.value = logo || "";
|
||||
const expirationTime = new Date().setHours(new Date().getHours() + 1);
|
||||
localStorage.setItem("icontitle_expiration_time", expirationTime);
|
||||
localStorage.setItem("icon", logo || "");
|
||||
localStorage.setItem("domainIcon", domainIcon || "");
|
||||
localStorage.setItem("title", siteName || "");
|
||||
applyFavicon(domainIcon || logo);
|
||||
if (siteName) {
|
||||
window.document.title = siteName + " - 运营后台";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getSite();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -12,19 +12,19 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { Platform } from "@element-plus/icons-vue";
|
||||
import i18n from "@/locale";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
|
||||
export default {
|
||||
name: "langSwitch",
|
||||
components: { Platform },
|
||||
methods: {
|
||||
langChange(v) {
|
||||
this.$i18n.locale = v;
|
||||
this.$store.commit("switchLang", v);
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "langSwitch" });
|
||||
|
||||
const appStore = useAppStore();
|
||||
|
||||
function langChange(v) {
|
||||
i18n.global.locale = v;
|
||||
appStore.switchLang(v);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -40,42 +40,42 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ArrowDown } from "@element-plus/icons-vue";
|
||||
|
||||
export default {
|
||||
name: "messageTip",
|
||||
components: { ArrowDown },
|
||||
props: {
|
||||
res: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
defineOptions({ name: "messageTip" });
|
||||
|
||||
const props = defineProps({
|
||||
res: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
computed: {
|
||||
value() {
|
||||
let count = 0;
|
||||
const r = this.res || {};
|
||||
Object.keys(r).forEach((k) => {
|
||||
if (r[k]) count += r[k];
|
||||
});
|
||||
return count;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
navigateTo(name) {
|
||||
if (name === "manager-appointment-work-orders") {
|
||||
this.$router.push({ name, query: { serviceStatus: "OPEN" } });
|
||||
return;
|
||||
}
|
||||
if (name === "manager-procurement-order-list") {
|
||||
this.$router.push({ name, query: { status: "SUBMITTED" } });
|
||||
return;
|
||||
}
|
||||
this.$router.push({ name });
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const value = computed(() => {
|
||||
let count = 0;
|
||||
const r = props.res || {};
|
||||
Object.keys(r).forEach((k) => {
|
||||
if (r[k]) count += r[k];
|
||||
});
|
||||
return count;
|
||||
});
|
||||
|
||||
function navigateTo(name) {
|
||||
if (name === "manager-appointment-work-orders") {
|
||||
router.push({ name, query: { serviceStatus: "OPEN" } });
|
||||
return;
|
||||
}
|
||||
if (name === "manager-procurement-order-list") {
|
||||
router.push({ name, query: { status: "SUBMITTED" } });
|
||||
return;
|
||||
}
|
||||
router.push({ name });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -109,4 +109,3 @@ export default {
|
||||
transform: translateY(-50%) translateX(50%);
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -18,8 +18,17 @@
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { otherRouter } from "@/router/router";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
|
||||
defineOptions({ name: "pageBreadcrumb" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const stripLayout = (name) => (name || "").replace(/__layout$/, "");
|
||||
|
||||
@@ -61,105 +70,101 @@ const findFirstLeaf = (group) => {
|
||||
return stripLayout(first?.name) || null;
|
||||
};
|
||||
|
||||
export default {
|
||||
name: "pageBreadcrumb",
|
||||
computed: {
|
||||
items() {
|
||||
const route = this.$route;
|
||||
if (!route || !route.name || route.name === "home_index") {
|
||||
return [{ title: "首页" }];
|
||||
}
|
||||
return this.buildChain(stripLayout(route.name));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
go(target) {
|
||||
if (!target || !target.name) return;
|
||||
if (this.$route.name === target.name) return;
|
||||
this.$router.push(target);
|
||||
},
|
||||
findInMenu(name) {
|
||||
const menuList = this.$store.state.app.menuList || [];
|
||||
for (const grp of menuList) {
|
||||
for (const child of grp.children || []) {
|
||||
if (stripLayout(child.name) === name) {
|
||||
return { group: grp, child };
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
routerEntry(name) {
|
||||
if (Array.isArray(otherRouter.children)) {
|
||||
return otherRouter.children.find((c) => c.name === name);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
topItem() {
|
||||
const navList = this.$store.state.app.navList || [];
|
||||
const menuList = this.$store.state.app.menuList || [];
|
||||
const currNavTitle = this.$store.state.app.currNavTitle;
|
||||
const firstName =
|
||||
this.$route.meta && this.$route.meta.firstRouterName;
|
||||
const items = computed(() => {
|
||||
if (!route || !route.name || route.name === "home_index") {
|
||||
return [{ title: "首页" }];
|
||||
}
|
||||
return buildChain(stripLayout(route.name));
|
||||
});
|
||||
|
||||
let topTitle = currNavTitle;
|
||||
if (firstName) {
|
||||
const top = navList.find((n) => n.name === firstName);
|
||||
if (top) topTitle = top.title;
|
||||
function go(target) {
|
||||
if (!target || !target.name) return;
|
||||
if (route.name === target.name) return;
|
||||
router.push(target);
|
||||
}
|
||||
|
||||
function findInMenu(name) {
|
||||
const menuList = appStore.menuList || [];
|
||||
for (const grp of menuList) {
|
||||
for (const child of grp.children || []) {
|
||||
if (stripLayout(child.name) === name) {
|
||||
return { group: grp, child };
|
||||
}
|
||||
if (!topTitle) return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
let target = null;
|
||||
if (menuList && menuList.length) {
|
||||
const leafName = findFirstLeaf(menuList[0]);
|
||||
if (leafName) target = { name: leafName };
|
||||
}
|
||||
return { title: topTitle, target };
|
||||
},
|
||||
buildChain(name, depth = 0) {
|
||||
if (depth > 6) return [];
|
||||
function routerEntry(name) {
|
||||
if (Array.isArray(otherRouter.children)) {
|
||||
return otherRouter.children.find((c) => c.name === name);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const inMenu = this.findInMenu(name);
|
||||
if (inMenu) {
|
||||
const list = [];
|
||||
const top = this.topItem();
|
||||
if (top) list.push(top);
|
||||
const groupLeaf = findFirstLeaf(inMenu.group);
|
||||
list.push({
|
||||
title: inMenu.group.title,
|
||||
target: groupLeaf ? { name: groupLeaf } : null,
|
||||
});
|
||||
list.push({ title: inMenu.child.title });
|
||||
return list;
|
||||
}
|
||||
function topItem() {
|
||||
const navList = appStore.navList || [];
|
||||
const menuList = appStore.menuList || [];
|
||||
const currNavTitle = appStore.currNavTitle;
|
||||
const firstName = route.meta && route.meta.firstRouterName;
|
||||
|
||||
const parentName = PARENT_MAP[name];
|
||||
const entry = this.routerEntry(name);
|
||||
const selfTitle = entry?.title
|
||||
? entry.title
|
||||
: (this.$route.meta && this.$route.meta.title
|
||||
? String(this.$route.meta.title).replace(/\s*-\s*.+$/, "")
|
||||
: "");
|
||||
let topTitle = currNavTitle;
|
||||
if (firstName) {
|
||||
const top = navList.find((n) => n.name === firstName);
|
||||
if (top) topTitle = top.title;
|
||||
}
|
||||
if (!topTitle) return null;
|
||||
|
||||
if (parentName) {
|
||||
const parentChain = this.buildChain(parentName, depth + 1).map(
|
||||
(it, idx, arr) =>
|
||||
idx === arr.length - 1
|
||||
? { title: it.title, target: { name: parentName } }
|
||||
: it
|
||||
);
|
||||
if (selfTitle) parentChain.push({ title: selfTitle });
|
||||
return parentChain;
|
||||
}
|
||||
let target = null;
|
||||
if (menuList && menuList.length) {
|
||||
const leafName = findFirstLeaf(menuList[0]);
|
||||
if (leafName) target = { name: leafName };
|
||||
}
|
||||
return { title: topTitle, target };
|
||||
}
|
||||
|
||||
const list = [];
|
||||
const top = this.topItem();
|
||||
if (top) list.push(top);
|
||||
if (selfTitle) list.push({ title: selfTitle });
|
||||
return list;
|
||||
},
|
||||
},
|
||||
};
|
||||
function buildChain(name, depth = 0) {
|
||||
if (depth > 6) return [];
|
||||
|
||||
const inMenu = findInMenu(name);
|
||||
if (inMenu) {
|
||||
const list = [];
|
||||
const top = topItem();
|
||||
if (top) list.push(top);
|
||||
const groupLeaf = findFirstLeaf(inMenu.group);
|
||||
list.push({
|
||||
title: inMenu.group.title,
|
||||
target: groupLeaf ? { name: groupLeaf } : null,
|
||||
});
|
||||
list.push({ title: inMenu.child.title });
|
||||
return list;
|
||||
}
|
||||
|
||||
const parentName = PARENT_MAP[name];
|
||||
const entry = routerEntry(name);
|
||||
const selfTitle = entry?.title
|
||||
? entry.title
|
||||
: (route.meta && route.meta.title
|
||||
? String(route.meta.title).replace(/\s*-\s*.+$/, "")
|
||||
: "");
|
||||
|
||||
if (parentName) {
|
||||
const parentChain = buildChain(parentName, depth + 1).map(
|
||||
(it, idx, arr) =>
|
||||
idx === arr.length - 1
|
||||
? { title: it.title, target: { name: parentName } }
|
||||
: it
|
||||
);
|
||||
if (selfTitle) parentChain.push({ title: selfTitle });
|
||||
return parentChain;
|
||||
}
|
||||
|
||||
const list = [];
|
||||
const top = topItem();
|
||||
if (top) list.push(top);
|
||||
if (selfTitle) list.push({ title: selfTitle });
|
||||
return list;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -31,88 +31,90 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, nextTick, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { setStore } from "@/libs/storage";
|
||||
import { Message } from "@/utils/message";
|
||||
import util from "@/libs/util.js";
|
||||
|
||||
export default {
|
||||
name: "shrinkableMenu",
|
||||
computed: {
|
||||
menuList() {
|
||||
return this.$store.state.app.menuList;
|
||||
},
|
||||
navList() {
|
||||
return this.$store.state.app.navList;
|
||||
},
|
||||
currNav() {
|
||||
return this.$store.state.app.currNav;
|
||||
},
|
||||
activeMenuName() {
|
||||
const route = this.$route;
|
||||
const routePath = util.normalizeRoutePath(route.path);
|
||||
for (const item of this.menuList) {
|
||||
for (const menu of item.children || []) {
|
||||
const menuPath = util.normalizeRoutePath(menu.path);
|
||||
if (menu.name === route.name || (menuPath && menuPath === routePath)) {
|
||||
return menu.name;
|
||||
}
|
||||
}
|
||||
defineOptions({ name: "shrinkableMenu" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const menuList = computed(() => appStore.menuList);
|
||||
const navList = computed(() => appStore.navList);
|
||||
const currNav = computed(() => appStore.currNav);
|
||||
const activeMenuName = computed(() => {
|
||||
const routePath = util.normalizeRoutePath(route.path);
|
||||
for (const item of menuList.value) {
|
||||
for (const menu of item.children || []) {
|
||||
const menuPath = util.normalizeRoutePath(menu.path);
|
||||
if (menu.name === route.name || (menuPath && menuPath === routePath)) {
|
||||
return menu.name;
|
||||
}
|
||||
return route.name;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(val) {
|
||||
if (
|
||||
val.meta.firstRouterName &&
|
||||
val.meta.firstRouterName !== this.currNav
|
||||
) {
|
||||
this.selectNav(val.meta.firstRouterName);
|
||||
}
|
||||
}
|
||||
return route.name;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.fullPath,
|
||||
() => {
|
||||
if (
|
||||
route.meta.firstRouterName &&
|
||||
route.meta.firstRouterName !== currNav.value
|
||||
) {
|
||||
selectNav(route.meta.firstRouterName);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const changeMenu = (name) => {
|
||||
if (!name) return;
|
||||
if (router.hasRoute(name)) {
|
||||
router.push({ name });
|
||||
return;
|
||||
}
|
||||
const menu = findMenuByName(name);
|
||||
if (menu?.path) {
|
||||
const path = menu.path.startsWith("/") ? menu.path : `/${menu.path}`;
|
||||
router.push({ path });
|
||||
return;
|
||||
}
|
||||
util.initRouter();
|
||||
nextTick(() => {
|
||||
if (router.hasRoute(name)) {
|
||||
router.push({ name });
|
||||
} else {
|
||||
Message.warning("菜单路由未就绪,请刷新页面后重试");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const findMenuByName = (name) => {
|
||||
for (const item of menuList.value) {
|
||||
for (const menu of item.children || []) {
|
||||
if (menu.name === name) {
|
||||
return menu;
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
changeMenu(name) {
|
||||
if (!name) return;
|
||||
if (this.$router.hasRoute(name)) {
|
||||
this.$router.push({ name });
|
||||
return;
|
||||
}
|
||||
const menu = this.findMenuByName(name);
|
||||
if (menu?.path) {
|
||||
const path = menu.path.startsWith("/") ? menu.path : `/${menu.path}`;
|
||||
this.$router.push({ path });
|
||||
return;
|
||||
}
|
||||
util.initRouter(this);
|
||||
this.$nextTick(() => {
|
||||
if (this.$router.hasRoute(name)) {
|
||||
this.$router.push({ name });
|
||||
} else {
|
||||
this.$Message.warning("菜单路由未就绪,请刷新页面后重试");
|
||||
}
|
||||
});
|
||||
},
|
||||
findMenuByName(name) {
|
||||
for (const item of this.menuList) {
|
||||
for (const menu of item.children || []) {
|
||||
if (menu.name === name) {
|
||||
return menu;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
selectNav(name) {
|
||||
this.$store.commit("setCurrNav", name);
|
||||
this.setStore("currNav", name);
|
||||
if (name === 'home_index') {
|
||||
this.$store.commit("updateMenulist", []);
|
||||
this.$router.push({ name: 'home_index' });
|
||||
return;
|
||||
}
|
||||
util.initRouter(this);
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
const selectNav = (name) => {
|
||||
appStore.setCurrNav(name);
|
||||
setStore("currNav", name);
|
||||
if (name === 'home_index') {
|
||||
appStore.updateMenulist([]);
|
||||
router.push({ name: 'home_index' });
|
||||
return;
|
||||
}
|
||||
util.initRouter();
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -41,160 +41,179 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, watch, onBeforeUnmount } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import util from "@/libs/util.js";
|
||||
|
||||
export default {
|
||||
name: "tagsPageOpened",
|
||||
props: {
|
||||
pageTagsList: Array,
|
||||
beforePush: {
|
||||
type: Function,
|
||||
default: () => true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentPageName: this.$route.name,
|
||||
tagBodyLeft: 0,
|
||||
visible: false,
|
||||
contextMenuLeft: 0,
|
||||
contextMenuTop: 0,
|
||||
actionList: {
|
||||
others: "关闭其他",
|
||||
clearAll: "关闭所有",
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
isActive(item) {
|
||||
const target = item.children ? item.children[0] : item;
|
||||
return util.isSameRoutePage(target, {
|
||||
name: this.currentPageName,
|
||||
path: this.$route.path,
|
||||
});
|
||||
},
|
||||
tagType(item) {
|
||||
return this.isActive(item) ? "primary" : "info";
|
||||
},
|
||||
itemTitle(item) {
|
||||
if (typeof item.title === "object") {
|
||||
return this.$t(item.title.i18n);
|
||||
}
|
||||
return item.title;
|
||||
},
|
||||
closePage(event, name) {
|
||||
let pageOpenedList = this.$store.state.app.pageOpenedList;
|
||||
let lastPageObj = pageOpenedList[0];
|
||||
if (this.currentPageName === name) {
|
||||
const len = pageOpenedList.length;
|
||||
for (let i = 1; i < len; i++) {
|
||||
if (pageOpenedList[i].name === name) {
|
||||
lastPageObj =
|
||||
i < len - 1 ? pageOpenedList[i + 1] : pageOpenedList[i - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (event && event.target) {
|
||||
const tagWidth = event.target.parentNode?.offsetWidth || 0;
|
||||
this.tagBodyLeft = Math.min(this.tagBodyLeft + tagWidth, 0);
|
||||
}
|
||||
this.$store.commit("removeTag", name);
|
||||
this.$store.commit("closePage", name);
|
||||
pageOpenedList = this.$store.state.app.pageOpenedList;
|
||||
localStorage.pageOpenedList = JSON.stringify(pageOpenedList);
|
||||
if (this.currentPageName === name) {
|
||||
this.linkTo(lastPageObj);
|
||||
}
|
||||
},
|
||||
linkTo(item) {
|
||||
const target = item.children ? item.children[0] : item;
|
||||
if (util.isSameRoutePage(target, this.$route)) return;
|
||||
if (!this.beforePush(item)) return;
|
||||
defineOptions({ name: "tagsPageOpened" });
|
||||
|
||||
const routerObj = { name: item.name };
|
||||
if (item.argu) routerObj.params = item.argu;
|
||||
if (item.query) routerObj.query = item.query;
|
||||
const props = defineProps({
|
||||
pageTagsList: Array,
|
||||
beforePush: {
|
||||
type: Function,
|
||||
default: () => true,
|
||||
},
|
||||
});
|
||||
|
||||
if (item.name && this.$router.hasRoute(item.name)) {
|
||||
this.$router.push(routerObj);
|
||||
return;
|
||||
}
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const path = target.path || item.path;
|
||||
if (path) {
|
||||
this.$router.push({
|
||||
path: path.startsWith("/") ? path : `/${path}`,
|
||||
query: item.query,
|
||||
});
|
||||
}
|
||||
},
|
||||
handlescroll(e) {
|
||||
const type = e.type;
|
||||
let delta = 0;
|
||||
if (type === "DOMMouseScroll" || type === "mousewheel") {
|
||||
delta = e.wheelDelta ? e.wheelDelta : -(e.detail || 0) * 40;
|
||||
}
|
||||
let left = 0;
|
||||
if (delta > 0) {
|
||||
left = Math.min(0, this.tagBodyLeft + delta);
|
||||
} else if (
|
||||
this.$refs.scrollCon.offsetWidth - 100 <
|
||||
this.$refs.scrollBody.offsetWidth
|
||||
) {
|
||||
if (
|
||||
this.tagBodyLeft <
|
||||
-(this.$refs.scrollBody.offsetWidth - this.$refs.scrollCon.offsetWidth + 100)
|
||||
) {
|
||||
left = this.tagBodyLeft;
|
||||
} else {
|
||||
left = Math.max(
|
||||
this.tagBodyLeft + delta,
|
||||
this.$refs.scrollCon.offsetWidth -
|
||||
this.$refs.scrollBody.offsetWidth -
|
||||
100
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.tagBodyLeft = 0;
|
||||
}
|
||||
this.tagBodyLeft = left;
|
||||
},
|
||||
handleTagsOption(type) {
|
||||
if (type === "clearAll") {
|
||||
this.$store.commit("clearAllTags");
|
||||
this.$router.push({ name: "home_index" });
|
||||
} else {
|
||||
this.$store.commit("clearOtherTags", this);
|
||||
}
|
||||
this.tagBodyLeft = 0;
|
||||
},
|
||||
contextMenu(item, e) {
|
||||
this.visible = true;
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left;
|
||||
this.contextMenuLeft = e.clientX - offsetLeft + 10;
|
||||
this.contextMenuTop = e.clientY - 64;
|
||||
},
|
||||
closeMenu() {
|
||||
this.visible = false;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(to) {
|
||||
this.currentPageName = to.name;
|
||||
},
|
||||
visible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener("click", this.closeMenu);
|
||||
} else {
|
||||
document.body.removeEventListener("click", this.closeMenu);
|
||||
}
|
||||
},
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.body.removeEventListener("click", this.closeMenu);
|
||||
},
|
||||
const currentPageName = ref(route.name);
|
||||
const tagBodyLeft = ref(0);
|
||||
const visible = ref(false);
|
||||
const contextMenuLeft = ref(0);
|
||||
const contextMenuTop = ref(0);
|
||||
const actionList = {
|
||||
others: "关闭其他",
|
||||
clearAll: "关闭所有",
|
||||
};
|
||||
|
||||
const scrollCon = ref(null);
|
||||
const scrollBody = ref(null);
|
||||
|
||||
const isActive = (item) => {
|
||||
const target = item.children ? item.children[0] : item;
|
||||
return util.isSameRoutePage(target, {
|
||||
name: currentPageName.value,
|
||||
path: route.path,
|
||||
});
|
||||
};
|
||||
|
||||
const tagType = (item) => {
|
||||
return isActive(item) ? "primary" : "info";
|
||||
};
|
||||
|
||||
const itemTitle = (item) => {
|
||||
if (typeof item.title === "object") {
|
||||
return t(item.title.i18n);
|
||||
}
|
||||
return item.title;
|
||||
};
|
||||
|
||||
const closePage = (event, name) => {
|
||||
let pageOpenedList = appStore.pageOpenedList;
|
||||
let lastPageObj = pageOpenedList[0];
|
||||
if (currentPageName.value === name) {
|
||||
const len = pageOpenedList.length;
|
||||
for (let i = 1; i < len; i++) {
|
||||
if (pageOpenedList[i].name === name) {
|
||||
lastPageObj =
|
||||
i < len - 1 ? pageOpenedList[i + 1] : pageOpenedList[i - 1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (event && event.target) {
|
||||
const tagWidth = event.target.parentNode?.offsetWidth || 0;
|
||||
tagBodyLeft.value = Math.min(tagBodyLeft.value + tagWidth, 0);
|
||||
}
|
||||
appStore.removeTag(name);
|
||||
appStore.closePage(name);
|
||||
pageOpenedList = appStore.pageOpenedList;
|
||||
localStorage.pageOpenedList = JSON.stringify(pageOpenedList);
|
||||
if (currentPageName.value === name) {
|
||||
linkTo(lastPageObj);
|
||||
}
|
||||
};
|
||||
|
||||
const linkTo = (item) => {
|
||||
const target = item.children ? item.children[0] : item;
|
||||
if (util.isSameRoutePage(target, route)) return;
|
||||
if (!props.beforePush(item)) return;
|
||||
|
||||
const routerObj = { name: item.name };
|
||||
if (item.argu) routerObj.params = item.argu;
|
||||
if (item.query) routerObj.query = item.query;
|
||||
|
||||
if (item.name && router.hasRoute(item.name)) {
|
||||
router.push(routerObj);
|
||||
return;
|
||||
}
|
||||
|
||||
const path = target.path || item.path;
|
||||
if (path) {
|
||||
router.push({
|
||||
path: path.startsWith("/") ? path : `/${path}`,
|
||||
query: item.query,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handlescroll = (e) => {
|
||||
const type = e.type;
|
||||
let delta = 0;
|
||||
if (type === "DOMMouseScroll" || type === "mousewheel") {
|
||||
delta = e.wheelDelta ? e.wheelDelta : -(e.detail || 0) * 40;
|
||||
}
|
||||
let left = 0;
|
||||
if (delta > 0) {
|
||||
left = Math.min(0, tagBodyLeft.value + delta);
|
||||
} else if (
|
||||
scrollCon.value.offsetWidth - 100 <
|
||||
scrollBody.value.offsetWidth
|
||||
) {
|
||||
if (
|
||||
tagBodyLeft.value <
|
||||
-(scrollBody.value.offsetWidth - scrollCon.value.offsetWidth + 100)
|
||||
) {
|
||||
left = tagBodyLeft.value;
|
||||
} else {
|
||||
left = Math.max(
|
||||
tagBodyLeft.value + delta,
|
||||
scrollCon.value.offsetWidth -
|
||||
scrollBody.value.offsetWidth -
|
||||
100
|
||||
);
|
||||
}
|
||||
} else {
|
||||
tagBodyLeft.value = 0;
|
||||
}
|
||||
tagBodyLeft.value = left;
|
||||
};
|
||||
|
||||
const handleTagsOption = (type) => {
|
||||
if (type === "clearAll") {
|
||||
appStore.clearAllTags();
|
||||
router.push({ name: "home_index" });
|
||||
} else {
|
||||
appStore.clearOtherTags(route.name);
|
||||
}
|
||||
tagBodyLeft.value = 0;
|
||||
};
|
||||
|
||||
const contextMenu = (item, e) => {
|
||||
visible.value = true;
|
||||
const offsetLeft = scrollCon.value.getBoundingClientRect().left;
|
||||
contextMenuLeft.value = e.clientX - offsetLeft + 10;
|
||||
contextMenuTop.value = e.clientY - 64;
|
||||
};
|
||||
|
||||
const closeMenu = () => {
|
||||
visible.value = false;
|
||||
};
|
||||
|
||||
watch(
|
||||
() => route.name,
|
||||
(name) => {
|
||||
currentPageName.value = name;
|
||||
}
|
||||
);
|
||||
|
||||
watch(visible, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener("click", closeMenu);
|
||||
} else {
|
||||
document.body.removeEventListener("click", closeMenu);
|
||||
}
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.body.removeEventListener("click", closeMenu);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -90,74 +90,74 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { getUserRecharge } from "@/api/member";
|
||||
|
||||
export default {
|
||||
name: "recharge",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
memberName: "",
|
||||
},
|
||||
selectDate: null,
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
rechargeWayText(way) {
|
||||
if (way === "ALIPAY") return "支付宝";
|
||||
if (way === "WECHAT") return "微信";
|
||||
if (way === "BANK_TRANSFER") return "线下转账";
|
||||
return "";
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
this.searchForm.startDate = v[0];
|
||||
this.searchForm.endDate = v[1];
|
||||
} else {
|
||||
this.searchForm.startDate = "";
|
||||
this.searchForm.endDate = "";
|
||||
}
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getUserRecharge(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "recharge" });
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
memberName: "",
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
function rechargeWayText(way) {
|
||||
if (way === "ALIPAY") return "支付宝";
|
||||
if (way === "WECHAT") return "微信";
|
||||
if (way === "BANK_TRANSFER") return "线下转账";
|
||||
return "";
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
searchForm.startDate = v[0];
|
||||
searchForm.endDate = v[1];
|
||||
} else {
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getUserRecharge(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -85,75 +85,75 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { getUserWallet } from "@/api/member";
|
||||
|
||||
export default {
|
||||
name: "walletLog",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
memberName: "",
|
||||
},
|
||||
selectDate: null,
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
serviceTypeText(type) {
|
||||
if (type === "WALLET_WITHDRAWAL") return "余额提现";
|
||||
if (type === "WALLET_PAY") return "余额支付";
|
||||
if (type === "WALLET_REFUND") return "余额退款";
|
||||
if (type === "WALLET_RECHARGE") return "余额充值";
|
||||
return "佣金提成";
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
this.searchForm.startDate = v[0];
|
||||
this.searchForm.endDate = v[1];
|
||||
} else {
|
||||
this.searchForm.startDate = "";
|
||||
this.searchForm.endDate = "";
|
||||
}
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getUserWallet(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "walletLog" });
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
memberName: "",
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
function serviceTypeText(type) {
|
||||
if (type === "WALLET_WITHDRAWAL") return "余额提现";
|
||||
if (type === "WALLET_PAY") return "余额支付";
|
||||
if (type === "WALLET_REFUND") return "余额退款";
|
||||
if (type === "WALLET_RECHARGE") return "余额充值";
|
||||
return "佣金提成";
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
searchForm.startDate = v[0];
|
||||
searchForm.endDate = v[1];
|
||||
} else {
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getUserWallet(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -169,168 +169,169 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { getUserWithdrawApply, withdrawApply } from "@/api/member";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "withdrawApply",
|
||||
data() {
|
||||
return {
|
||||
modalTitle: "",
|
||||
loading: true,
|
||||
audit: "",
|
||||
roleModalVisible: false,
|
||||
queryModalVisible: false,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
memberName: "",
|
||||
applyStatus: "",
|
||||
},
|
||||
selectDate: null,
|
||||
submitLoading: false,
|
||||
selectList: [],
|
||||
selectCount: 0,
|
||||
showList: {},
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
paramTypeFilter(val) {
|
||||
if (val === "APPLY") return "申请中";
|
||||
if (val === "VIA_AUDITING") return "审核通过(提现成功)";
|
||||
if (val === "WAIT_USER_CONFIRM") return "用户确认";
|
||||
if (val === "FAIL_AUDITING") return "审核拒绝";
|
||||
if (val === "SUCCESS") return "提现成功";
|
||||
if (val === "ERROR") return "提现失败";
|
||||
return "未知状态";
|
||||
},
|
||||
applyStatusText(status) {
|
||||
if (status === "APPLY") return "申请中";
|
||||
if (status === "VIA_AUDITING") return "审核通过";
|
||||
if (status === "WAIT_USER_CONFIRM") return "用户确认";
|
||||
if (status === "SUCCESS") return "提现成功";
|
||||
if (status === "ERROR") return "提现失败";
|
||||
return "审核拒绝";
|
||||
},
|
||||
applyStatusTagType(status) {
|
||||
if (status === "APPLY") return "warning";
|
||||
if (status === "VIA_AUDITING") return "success";
|
||||
if (status === "WAIT_USER_CONFIRM") return "warning";
|
||||
if (status === "SUCCESS") return "primary";
|
||||
if (status === "ERROR") return "primary";
|
||||
return "danger";
|
||||
},
|
||||
openAudit(row) {
|
||||
this.showList = { ...row };
|
||||
this.audit = "";
|
||||
this.roleModalVisible = true;
|
||||
},
|
||||
openQuery(row) {
|
||||
this.showList = { ...row };
|
||||
this.modalTitle = "查看";
|
||||
this.queryModalVisible = true;
|
||||
},
|
||||
submitRole(res) {
|
||||
const params = {};
|
||||
params.applyId = this.showList.id;
|
||||
params.result = res;
|
||||
params.remark = this.audit;
|
||||
if (res === false && params.remark === "") {
|
||||
ElMessage.error("审核备注不能为空");
|
||||
return;
|
||||
defineOptions({ name: "withdrawApply" });
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
// 模板 el-table 的 ref
|
||||
const table = ref(null);
|
||||
|
||||
const modalTitle = ref("");
|
||||
const loading = ref(true);
|
||||
const audit = ref("");
|
||||
const roleModalVisible = ref(false);
|
||||
const queryModalVisible = ref(false);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
memberName: "",
|
||||
applyStatus: "",
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const submitLoading = ref(false);
|
||||
const selectList = ref([]);
|
||||
const selectCount = ref(0);
|
||||
const showList = ref({});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
function paramTypeFilter(val) {
|
||||
if (val === "APPLY") return "申请中";
|
||||
if (val === "VIA_AUDITING") return "审核通过(提现成功)";
|
||||
if (val === "WAIT_USER_CONFIRM") return "用户确认";
|
||||
if (val === "FAIL_AUDITING") return "审核拒绝";
|
||||
if (val === "SUCCESS") return "提现成功";
|
||||
if (val === "ERROR") return "提现失败";
|
||||
return "未知状态";
|
||||
}
|
||||
function applyStatusText(status) {
|
||||
if (status === "APPLY") return "申请中";
|
||||
if (status === "VIA_AUDITING") return "审核通过";
|
||||
if (status === "WAIT_USER_CONFIRM") return "用户确认";
|
||||
if (status === "SUCCESS") return "提现成功";
|
||||
if (status === "ERROR") return "提现失败";
|
||||
return "审核拒绝";
|
||||
}
|
||||
function applyStatusTagType(status) {
|
||||
if (status === "APPLY") return "warning";
|
||||
if (status === "VIA_AUDITING") return "success";
|
||||
if (status === "WAIT_USER_CONFIRM") return "warning";
|
||||
if (status === "SUCCESS") return "primary";
|
||||
if (status === "ERROR") return "primary";
|
||||
return "danger";
|
||||
}
|
||||
function openAudit(row) {
|
||||
showList.value = { ...row };
|
||||
audit.value = "";
|
||||
roleModalVisible.value = true;
|
||||
}
|
||||
function openQuery(row) {
|
||||
showList.value = { ...row };
|
||||
modalTitle.value = "查看";
|
||||
queryModalVisible.value = true;
|
||||
}
|
||||
function submitRole(res) {
|
||||
const params = {};
|
||||
params.applyId = showList.value.id;
|
||||
params.result = res;
|
||||
params.remark = audit.value;
|
||||
if (res === false && params.remark === "") {
|
||||
ElMessage.error("审核备注不能为空");
|
||||
return;
|
||||
}
|
||||
submitLoading.value = true;
|
||||
withdrawApply(params)
|
||||
.then((result) => {
|
||||
const success = result === true || (result && result.success === true);
|
||||
if (success) {
|
||||
ElMessage.success("操作成功");
|
||||
roleModalVisible.value = false;
|
||||
queryModalVisible.value = false;
|
||||
audit.value = "";
|
||||
showList.value = {};
|
||||
getDataList();
|
||||
}
|
||||
this.submitLoading = true;
|
||||
withdrawApply(params)
|
||||
.then((result) => {
|
||||
const success = result === true || (result && result.success === true);
|
||||
if (success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.roleModalVisible = false;
|
||||
this.queryModalVisible = false;
|
||||
this.audit = "";
|
||||
this.showList = {};
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.submitLoading = false;
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
this.clearSelectAll();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
handleReset() {
|
||||
this.$refs.searchForm.resetFields();
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.selectDate = null;
|
||||
this.searchForm.startDate = "";
|
||||
this.searchForm.endDate = "";
|
||||
this.searchForm.memberName = "";
|
||||
this.getDataList();
|
||||
},
|
||||
changeSort(e) {
|
||||
this.searchForm.sort = e.prop || e.key;
|
||||
this.searchForm.order = e.order === "ascending" ? "asc" : e.order === "descending" ? "desc" : "";
|
||||
if (!e.order) {
|
||||
this.searchForm.order = "";
|
||||
}
|
||||
this.getDataList();
|
||||
},
|
||||
clearSelectAll() {
|
||||
if (this.$refs.table) {
|
||||
this.$refs.table.clearSelection();
|
||||
}
|
||||
},
|
||||
changeSelect(e) {
|
||||
this.selectList = e;
|
||||
this.selectCount = e.length;
|
||||
},
|
||||
selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
this.searchForm.startDate = v[0];
|
||||
this.searchForm.endDate = v[1];
|
||||
} else {
|
||||
this.searchForm.startDate = "";
|
||||
this.searchForm.endDate = "";
|
||||
}
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
getUserWithdrawApply(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
clearSelectAll();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function handleReset() {
|
||||
searchFormRef.value.resetFields();
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
selectDate.value = null;
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
searchForm.memberName = "";
|
||||
getDataList();
|
||||
}
|
||||
function changeSort(e) {
|
||||
searchForm.sort = e.prop || e.key;
|
||||
searchForm.order = e.order === "ascending" ? "asc" : e.order === "descending" ? "desc" : "";
|
||||
if (!e.order) {
|
||||
searchForm.order = "";
|
||||
}
|
||||
getDataList();
|
||||
}
|
||||
function clearSelectAll() {
|
||||
if (table.value) {
|
||||
table.value.clearSelection();
|
||||
}
|
||||
}
|
||||
function changeSelect(e) {
|
||||
selectList.value = e;
|
||||
selectCount.value = e.length;
|
||||
}
|
||||
function selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
searchForm.startDate = v[0];
|
||||
searchForm.endDate = v[1];
|
||||
} else {
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
getUserWithdrawApply(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.ops a {
|
||||
|
||||
@@ -343,7 +343,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, computed, nextTick, onMounted } from "vue";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
import { getPlatformCoupon } from "@/api/promotion";
|
||||
import { formatPromotionCouponValidityHtml } from "@/utils/promotions";
|
||||
@@ -351,6 +352,8 @@ import couponTemplate from "@/views/promotions/coupon/coupon.vue";
|
||||
import uploadPicInput from "@/components/lili/upload-pic-input.vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
defineOptions({ name: "memberBenefit" });
|
||||
|
||||
const GIFT_POINT = "GIFT_POINT";
|
||||
const COUPON_PACKAGE = "COUPON_PACKAGE";
|
||||
|
||||
@@ -368,496 +371,490 @@ const buildDefaultForm = () => ({
|
||||
couponPackageRows: [],
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "memberBenefit",
|
||||
components: {
|
||||
uploadPicInput,
|
||||
couponTemplate,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
benefitTypeOptions: [],
|
||||
loading: false,
|
||||
total: 0,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
// 模板 ref(couponPicker 为优惠券选择组件,addForm/editForm 为表单)
|
||||
const couponPicker = ref(null);
|
||||
const addForm = ref(null);
|
||||
const editForm = ref(null);
|
||||
// 动态 ref 映射表(原 this.$refs[refName])
|
||||
const formRefs = { addForm, editForm };
|
||||
|
||||
const benefitTypeOptions = ref([]);
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const data = ref([]);
|
||||
const addFlag = ref(false);
|
||||
const editFlag = ref(false);
|
||||
const submitAddLoading = ref(false);
|
||||
const submitEditLoading = ref(false);
|
||||
const formAdd = ref(buildDefaultForm());
|
||||
const formEdit = ref(buildDefaultForm());
|
||||
const couponPickerVisible = ref(false);
|
||||
const couponPickerWhich = ref("add");
|
||||
|
||||
// 动态表单映射表(原 this[formKey])
|
||||
const formMap = { formAdd, formEdit };
|
||||
|
||||
const rulesAdd = computed(() => buildFormRules("add"));
|
||||
const rulesEdit = computed(() => buildFormRules("edit"));
|
||||
const couponPickerSelectedList = computed(() => {
|
||||
const form = couponPickerWhich.value === "add" ? formAdd.value : formEdit.value;
|
||||
return (form.couponPackageRows || []).map((r) => ({
|
||||
id: r.couponId,
|
||||
couponName: r.couponName || "",
|
||||
}));
|
||||
});
|
||||
|
||||
function benefitTypeLabel(v) {
|
||||
if (!v) return "-";
|
||||
const opt = benefitTypeOptions.value.find((o) => o.value === v);
|
||||
return opt ? opt.description : v;
|
||||
}
|
||||
function touchFormField(refName, field) {
|
||||
nextTick(() => {
|
||||
const ref = formRefs[refName]?.value;
|
||||
if (ref && typeof ref.validateField === "function") {
|
||||
// validateField rejects on failure; catch to avoid dev-server [object Object] overlay
|
||||
ref.validateField(field, () => {});
|
||||
}
|
||||
});
|
||||
}
|
||||
function onCouponQuantityChange(which, index, val) {
|
||||
const formKey = which === "add" ? "formAdd" : "formEdit";
|
||||
const refName = which === "add" ? "addForm" : "editForm";
|
||||
formMap[formKey].value.couponPackageRows[index].quantity = val;
|
||||
touchFormField(refName, "couponPackageRows");
|
||||
}
|
||||
function buildFormRules(which) {
|
||||
const formKey = which === "add" ? "formAdd" : "formEdit";
|
||||
return {
|
||||
benefitType: [{ required: true, message: "请选择权益类型", trigger: "change" }],
|
||||
benefitName: [{ required: true, message: "请输入权益名称", trigger: "blur" }],
|
||||
benefitLogo: [{ required: true, message: "请配置权益LOGO", trigger: "change" }],
|
||||
benefitSort: [{ required: true, type: "number", message: "请输入排序", trigger: "change" }],
|
||||
benefitState: [{ required: true, message: "请选择启用状态", trigger: "change" }],
|
||||
giftPoint: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
const form = formMap[formKey].value;
|
||||
validateGiftPoint(form, value, callback);
|
||||
},
|
||||
trigger: ["change", "blur"],
|
||||
},
|
||||
data: [],
|
||||
addFlag: false,
|
||||
editFlag: false,
|
||||
submitAddLoading: false,
|
||||
submitEditLoading: false,
|
||||
formAdd: buildDefaultForm(),
|
||||
formEdit: buildDefaultForm(),
|
||||
couponPickerVisible: false,
|
||||
couponPickerWhich: "add",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
rulesAdd() {
|
||||
return this.buildFormRules("add");
|
||||
},
|
||||
rulesEdit() {
|
||||
return this.buildFormRules("edit");
|
||||
},
|
||||
couponPickerSelectedList() {
|
||||
const form = this.couponPickerWhich === "add" ? this.formAdd : this.formEdit;
|
||||
return (form.couponPackageRows || []).map((r) => ({
|
||||
id: r.couponId,
|
||||
couponName: r.couponName || "",
|
||||
],
|
||||
couponPackageRows: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
const form = formMap[formKey].value;
|
||||
validateCouponPackage(form, callback);
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
function validateGiftPoint(form, value, callback) {
|
||||
if (form.benefitType !== GIFT_POINT) return callback();
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return callback(new Error("请输入赠送积分"));
|
||||
}
|
||||
const n = Number(value);
|
||||
if (Number.isNaN(n) || n < 0 || n > 99999) {
|
||||
return callback(new Error("赠送积分范围为 0-99999"));
|
||||
}
|
||||
callback();
|
||||
}
|
||||
function validateCouponPackage(form, callback) {
|
||||
if (form.benefitType !== COUPON_PACKAGE) return callback();
|
||||
const rows = form.couponPackageRows || [];
|
||||
if (!rows.length) {
|
||||
return callback(new Error("请添加至少一张优惠券"));
|
||||
}
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const q = Number(rows[i].quantity);
|
||||
if (Number.isNaN(q) || q < 1 || q > 10) {
|
||||
return callback(new Error("每张券赠送张数需在 1~10 之间"));
|
||||
}
|
||||
}
|
||||
callback();
|
||||
}
|
||||
function formatCouponFace(row) {
|
||||
if (!row) return "-";
|
||||
if (row.price !== undefined && row.price !== null && row.price !== "") {
|
||||
return `¥${row.price}`;
|
||||
}
|
||||
if (row.couponDiscount !== undefined && row.couponDiscount !== null && row.couponDiscount !== "") {
|
||||
return `${row.couponDiscount}折`;
|
||||
}
|
||||
return "-";
|
||||
}
|
||||
function formatCouponValidity(row) {
|
||||
return formatPromotionCouponValidityHtml(row);
|
||||
}
|
||||
function buildCouponDisplayRow(detail, quantity) {
|
||||
const qty = Math.min(10, Math.max(1, Number(quantity) || 1));
|
||||
return {
|
||||
couponId: detail.id,
|
||||
quantity: qty,
|
||||
couponName: detail.couponName || "",
|
||||
faceValueLabel: formatCouponFace(detail),
|
||||
validRange: formatCouponValidity(detail),
|
||||
};
|
||||
}
|
||||
function buildBenefitConfigString(slice) {
|
||||
const { benefitType, giftPoint, couponPackageRows } = slice;
|
||||
if (benefitType === GIFT_POINT) {
|
||||
return JSON.stringify({ giftPoint: Number(giftPoint) });
|
||||
}
|
||||
if (benefitType === COUPON_PACKAGE) {
|
||||
const coupons = (couponPackageRows || []).map((r) => ({
|
||||
couponId: String(r.couponId),
|
||||
quantity: Math.min(10, Math.max(1, Number(r.quantity) || 1)),
|
||||
}));
|
||||
return JSON.stringify({ coupons });
|
||||
}
|
||||
return "";
|
||||
}
|
||||
function parseGiftPointFromConfig(benefitType, benefitConfigStr) {
|
||||
if (benefitType !== GIFT_POINT || !benefitConfigStr) return null;
|
||||
try {
|
||||
const o = JSON.parse(benefitConfigStr);
|
||||
if (o && typeof o.giftPoint !== "undefined") {
|
||||
const n = Number(o.giftPoint);
|
||||
return Number.isNaN(n) ? null : n;
|
||||
}
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function onAddBenefitTypeChange(val) {
|
||||
if (val !== GIFT_POINT) {
|
||||
formAdd.value.giftPoint = null;
|
||||
}
|
||||
if (val !== COUPON_PACKAGE) {
|
||||
formAdd.value.couponPackageRows = [];
|
||||
}
|
||||
if (addForm.value && val !== GIFT_POINT) {
|
||||
touchFormField("addForm", "giftPoint");
|
||||
}
|
||||
if (addForm.value && val !== COUPON_PACKAGE) {
|
||||
touchFormField("addForm", "couponPackageRows");
|
||||
}
|
||||
}
|
||||
function onEditBenefitTypeChange(val) {
|
||||
if (val !== GIFT_POINT) {
|
||||
formEdit.value.giftPoint = null;
|
||||
}
|
||||
if (val !== COUPON_PACKAGE) {
|
||||
formEdit.value.couponPackageRows = [];
|
||||
}
|
||||
if (editForm.value && val !== GIFT_POINT) {
|
||||
touchFormField("editForm", "giftPoint");
|
||||
}
|
||||
if (editForm.value && val !== COUPON_PACKAGE) {
|
||||
touchFormField("editForm", "couponPackageRows");
|
||||
}
|
||||
}
|
||||
function parseCouponsFromConfig(str) {
|
||||
if (!str) return [];
|
||||
try {
|
||||
const o = JSON.parse(str);
|
||||
if (o && Array.isArray(o.coupons)) {
|
||||
return o.coupons.map((c) => ({
|
||||
couponId: c.couponId,
|
||||
quantity: c.quantity,
|
||||
}));
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
benefitTypeLabel(v) {
|
||||
if (!v) return "-";
|
||||
const opt = this.benefitTypeOptions.find((o) => o.value === v);
|
||||
return opt ? opt.description : v;
|
||||
},
|
||||
touchFormField(refName, field) {
|
||||
this.$nextTick(() => {
|
||||
const ref = this.$refs[refName];
|
||||
if (ref && typeof ref.validateField === "function") {
|
||||
// validateField rejects on failure; catch to avoid dev-server [object Object] overlay
|
||||
ref.validateField(field, () => {});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
return [];
|
||||
}
|
||||
function normalizeCouponFaceFromDetail(item) {
|
||||
let face = formatCouponFace(item);
|
||||
if (face !== "-") return face;
|
||||
const raw = item.faceValueText;
|
||||
if (raw == null || raw === "") return "-";
|
||||
return String(raw)
|
||||
.replace(/^减免现金\s*/, "")
|
||||
.replace(/^减免\s*/, "")
|
||||
.trim() || "-";
|
||||
}
|
||||
function mapCouponItemsToPackageRows(items) {
|
||||
if (!Array.isArray(items)) return [];
|
||||
return items.map((item) => {
|
||||
const face = normalizeCouponFaceFromDetail(item);
|
||||
const validHtml = item.validityText
|
||||
? String(item.validityText).replace(/\n/g, "<br/>")
|
||||
: "-";
|
||||
return {
|
||||
couponId: item.couponId,
|
||||
quantity: Math.min(10, Math.max(1, Number(item.quantity) || 1)),
|
||||
couponName: item.couponName || "",
|
||||
faceValueLabel: face,
|
||||
validRange: validHtml,
|
||||
};
|
||||
});
|
||||
}
|
||||
async function hydrateCouponRowsForEdit(snippets) {
|
||||
const rows = [];
|
||||
for (const s of snippets) {
|
||||
const cid = s.couponId;
|
||||
const qty = s.quantity;
|
||||
try {
|
||||
const res = await getPlatformCoupon(cid);
|
||||
if (res && res.success && res.result) {
|
||||
rows.push(buildCouponDisplayRow(res.result, qty));
|
||||
} else {
|
||||
rows.push({
|
||||
couponId: cid,
|
||||
quantity: Math.min(10, Math.max(1, Number(qty) || 1)),
|
||||
couponName: String(cid),
|
||||
faceValueLabel: "-",
|
||||
validRange: "-",
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
rows.push({
|
||||
couponId: cid,
|
||||
quantity: Math.min(10, Math.max(1, Number(qty) || 1)),
|
||||
couponName: String(cid),
|
||||
faceValueLabel: "-",
|
||||
validRange: "-",
|
||||
});
|
||||
},
|
||||
onCouponQuantityChange(which, index, val) {
|
||||
const formKey = which === "add" ? "formAdd" : "formEdit";
|
||||
const refName = which === "add" ? "addForm" : "editForm";
|
||||
this[formKey].couponPackageRows[index].quantity = val;
|
||||
this.touchFormField(refName, "couponPackageRows");
|
||||
},
|
||||
buildFormRules(which) {
|
||||
const formKey = which === "add" ? "formAdd" : "formEdit";
|
||||
return {
|
||||
benefitType: [{ required: true, message: "请选择权益类型", trigger: "change" }],
|
||||
benefitName: [{ required: true, message: "请输入权益名称", trigger: "blur" }],
|
||||
benefitLogo: [{ required: true, message: "请配置权益LOGO", trigger: "change" }],
|
||||
benefitSort: [{ required: true, type: "number", message: "请输入排序", trigger: "change" }],
|
||||
benefitState: [{ required: true, message: "请选择启用状态", trigger: "change" }],
|
||||
giftPoint: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
const form = this[formKey];
|
||||
this.validateGiftPoint(form, value, callback);
|
||||
},
|
||||
trigger: ["change", "blur"],
|
||||
},
|
||||
],
|
||||
couponPackageRows: [
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
const form = this[formKey];
|
||||
this.validateCouponPackage(form, callback);
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
formEdit.value.couponPackageRows = rows;
|
||||
}
|
||||
function handleCouponPickerClose() {
|
||||
couponPickerVisible.value = false;
|
||||
}
|
||||
function openCouponPicker(which) {
|
||||
couponPickerWhich.value = which;
|
||||
couponPickerVisible.value = true;
|
||||
}
|
||||
function confirmCouponPicker() {
|
||||
const list = couponPicker.value?.getSelection?.() || [];
|
||||
if (!list.length) {
|
||||
ElMessage.warning("请至少选择一张优惠券");
|
||||
return;
|
||||
}
|
||||
onCouponTemplateSelected(list);
|
||||
couponPickerVisible.value = false;
|
||||
}
|
||||
function onCouponTemplateSelected(selectedRows) {
|
||||
const which = couponPickerWhich.value;
|
||||
const form = which === "add" ? formAdd.value : formEdit.value;
|
||||
const refName = which === "add" ? "addForm" : "editForm";
|
||||
const list = selectedRows || [];
|
||||
const rows = list.map((row) => {
|
||||
const existing = (form.couponPackageRows || []).find((r) => String(r.couponId) === String(row.id));
|
||||
const qty = existing ? existing.quantity : 1;
|
||||
return buildCouponDisplayRow(row, qty);
|
||||
});
|
||||
form.couponPackageRows = rows;
|
||||
touchFormField(refName, "couponPackageRows");
|
||||
}
|
||||
function removeCouponRow(which, index) {
|
||||
const form = which === "add" ? formAdd.value : formEdit.value;
|
||||
const refName = which === "add" ? "addForm" : "editForm";
|
||||
form.couponPackageRows.splice(index, 1);
|
||||
touchFormField(refName, "couponPackageRows");
|
||||
}
|
||||
function init() {
|
||||
loadBenefitTypes();
|
||||
getData();
|
||||
}
|
||||
function loadBenefitTypes() {
|
||||
API_Member.getMemberBenefitTypes().then((res) => {
|
||||
if (res && res.success && Array.isArray(res.result)) {
|
||||
benefitTypeOptions.value = res.result;
|
||||
} else {
|
||||
benefitTypeOptions.value = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
function changePage(page) {
|
||||
searchForm.value.pageNumber = page;
|
||||
getData();
|
||||
}
|
||||
function changePageSize(size) {
|
||||
searchForm.value.pageSize = size;
|
||||
searchForm.value.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
const params = { ...searchForm.value };
|
||||
API_Member.getMemberBenefitByPage(params).then((res) => {
|
||||
loading.value = false;
|
||||
if (res && res.success && res.result) {
|
||||
data.value = Array.isArray(res.result.records) ? res.result.records : [];
|
||||
total.value = Number(res.result.total) || 0;
|
||||
} else {
|
||||
data.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
function openAdd() {
|
||||
addFlag.value = true;
|
||||
submitAddLoading.value = false;
|
||||
nextTick(() => {
|
||||
if (addForm.value) addForm.value.resetFields();
|
||||
formAdd.value = buildDefaultForm();
|
||||
});
|
||||
}
|
||||
function submitAdd() {
|
||||
if (!addForm.value) {
|
||||
ElMessage.warning("表单未就绪,请稍后重试");
|
||||
return;
|
||||
}
|
||||
addForm.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitAddLoading.value = true;
|
||||
const { giftPoint, couponPackageRows, ...rest } = formAdd.value;
|
||||
const payload = {
|
||||
...rest,
|
||||
benefitConfig: buildBenefitConfigString({
|
||||
benefitType: formAdd.value.benefitType,
|
||||
giftPoint,
|
||||
couponPackageRows,
|
||||
}),
|
||||
};
|
||||
API_Member.addMemberBenefit(payload).then((res) => {
|
||||
submitAddLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
addFlag.value = false;
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
} else {
|
||||
ElMessage.error("添加失败");
|
||||
}
|
||||
}).catch(() => {
|
||||
submitAddLoading.value = false;
|
||||
ElMessage.error("网络异常,请稍后重试");
|
||||
});
|
||||
});
|
||||
}
|
||||
function openEdit(row) {
|
||||
editFlag.value = true;
|
||||
submitEditLoading.value = false;
|
||||
nextTick(() => {
|
||||
if (editForm.value) editForm.value.resetFields();
|
||||
});
|
||||
API_Member.getMemberBenefit(row.id).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
const raw = res.result;
|
||||
const detail = raw.benefit != null ? raw.benefit : raw;
|
||||
const couponItems = raw.couponItems;
|
||||
const bt = detail.benefitType || "";
|
||||
const parsed = parseGiftPointFromConfig(bt, detail.benefitConfig || "");
|
||||
const couponSnippets = parseCouponsFromConfig(detail.benefitConfig || "");
|
||||
|
||||
let couponRows = [];
|
||||
if (bt === COUPON_PACKAGE && Array.isArray(couponItems) && couponItems.length) {
|
||||
couponRows = mapCouponItemsToPackageRows(couponItems);
|
||||
}
|
||||
|
||||
formEdit.value = {
|
||||
id: detail.id || row.id || "",
|
||||
benefitName: detail.benefitName || "",
|
||||
benefitCode: detail.benefitCode || "",
|
||||
benefitType: bt,
|
||||
benefitLogo: detail.benefitLogo || "",
|
||||
benefitDesc: detail.benefitDesc || "",
|
||||
benefitSort: Number(detail.benefitSort) > 0 ? Number(detail.benefitSort) : 1,
|
||||
benefitState: detail.benefitState || "OPEN",
|
||||
benefitConfig: detail.benefitConfig || "",
|
||||
giftPoint: bt === GIFT_POINT ? (parsed !== null ? parsed : null) : null,
|
||||
couponPackageRows: couponRows,
|
||||
};
|
||||
},
|
||||
validateGiftPoint(form, value, callback) {
|
||||
if (form.benefitType !== GIFT_POINT) return callback();
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return callback(new Error("请输入赠送积分"));
|
||||
|
||||
if (bt === COUPON_PACKAGE && couponRows.length === 0 && couponSnippets.length) {
|
||||
hydrateCouponRowsForEdit(couponSnippets);
|
||||
}
|
||||
const n = Number(value);
|
||||
if (Number.isNaN(n) || n < 0 || n > 99999) {
|
||||
return callback(new Error("赠送积分范围为 0-99999"));
|
||||
}
|
||||
});
|
||||
}
|
||||
function submitEdit() {
|
||||
if (!editForm.value) {
|
||||
ElMessage.warning("表单未就绪,请稍后重试");
|
||||
return;
|
||||
}
|
||||
editForm.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitEditLoading.value = true;
|
||||
const { id, giftPoint, couponPackageRows, ...rest } = formEdit.value;
|
||||
const payload = {
|
||||
...rest,
|
||||
benefitConfig: buildBenefitConfigString({
|
||||
benefitType: formEdit.value.benefitType,
|
||||
giftPoint,
|
||||
couponPackageRows,
|
||||
}),
|
||||
};
|
||||
API_Member.updateMemberBenefit(id, payload).then((res) => {
|
||||
submitEditLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
editFlag.value = false;
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
} else {
|
||||
ElMessage.error("修改失败");
|
||||
}
|
||||
callback();
|
||||
},
|
||||
validateCouponPackage(form, callback) {
|
||||
if (form.benefitType !== COUPON_PACKAGE) return callback();
|
||||
const rows = form.couponPackageRows || [];
|
||||
if (!rows.length) {
|
||||
return callback(new Error("请添加至少一张优惠券"));
|
||||
}
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const q = Number(rows[i].quantity);
|
||||
if (Number.isNaN(q) || q < 1 || q > 10) {
|
||||
return callback(new Error("每张券赠送张数需在 1~10 之间"));
|
||||
}
|
||||
}
|
||||
callback();
|
||||
},
|
||||
formatCouponFace(row) {
|
||||
if (!row) return "-";
|
||||
if (row.price !== undefined && row.price !== null && row.price !== "") {
|
||||
return `¥${row.price}`;
|
||||
}
|
||||
if (row.couponDiscount !== undefined && row.couponDiscount !== null && row.couponDiscount !== "") {
|
||||
return `${row.couponDiscount}折`;
|
||||
}
|
||||
return "-";
|
||||
},
|
||||
formatCouponValidity(row) {
|
||||
return formatPromotionCouponValidityHtml(row);
|
||||
},
|
||||
buildCouponDisplayRow(detail, quantity) {
|
||||
const qty = Math.min(10, Math.max(1, Number(quantity) || 1));
|
||||
return {
|
||||
couponId: detail.id,
|
||||
quantity: qty,
|
||||
couponName: detail.couponName || "",
|
||||
faceValueLabel: this.formatCouponFace(detail),
|
||||
validRange: this.formatCouponValidity(detail),
|
||||
};
|
||||
},
|
||||
buildBenefitConfigString(slice) {
|
||||
const { benefitType, giftPoint, couponPackageRows } = slice;
|
||||
if (benefitType === GIFT_POINT) {
|
||||
return JSON.stringify({ giftPoint: Number(giftPoint) });
|
||||
}
|
||||
if (benefitType === COUPON_PACKAGE) {
|
||||
const coupons = (couponPackageRows || []).map((r) => ({
|
||||
couponId: String(r.couponId),
|
||||
quantity: Math.min(10, Math.max(1, Number(r.quantity) || 1)),
|
||||
}));
|
||||
return JSON.stringify({ coupons });
|
||||
}
|
||||
return "";
|
||||
},
|
||||
parseGiftPointFromConfig(benefitType, benefitConfigStr) {
|
||||
if (benefitType !== GIFT_POINT || !benefitConfigStr) return null;
|
||||
try {
|
||||
const o = JSON.parse(benefitConfigStr);
|
||||
if (o && typeof o.giftPoint !== "undefined") {
|
||||
const n = Number(o.giftPoint);
|
||||
return Number.isNaN(n) ? null : n;
|
||||
}
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onAddBenefitTypeChange(val) {
|
||||
if (val !== GIFT_POINT) {
|
||||
this.formAdd.giftPoint = null;
|
||||
}
|
||||
if (val !== COUPON_PACKAGE) {
|
||||
this.formAdd.couponPackageRows = [];
|
||||
}
|
||||
if (this.$refs.addForm && val !== GIFT_POINT) {
|
||||
this.touchFormField("addForm", "giftPoint");
|
||||
}
|
||||
if (this.$refs.addForm && val !== COUPON_PACKAGE) {
|
||||
this.touchFormField("addForm", "couponPackageRows");
|
||||
}
|
||||
},
|
||||
onEditBenefitTypeChange(val) {
|
||||
if (val !== GIFT_POINT) {
|
||||
this.formEdit.giftPoint = null;
|
||||
}
|
||||
if (val !== COUPON_PACKAGE) {
|
||||
this.formEdit.couponPackageRows = [];
|
||||
}
|
||||
if (this.$refs.editForm && val !== GIFT_POINT) {
|
||||
this.touchFormField("editForm", "giftPoint");
|
||||
}
|
||||
if (this.$refs.editForm && val !== COUPON_PACKAGE) {
|
||||
this.touchFormField("editForm", "couponPackageRows");
|
||||
}
|
||||
},
|
||||
parseCouponsFromConfig(str) {
|
||||
if (!str) return [];
|
||||
try {
|
||||
const o = JSON.parse(str);
|
||||
if (o && Array.isArray(o.coupons)) {
|
||||
return o.coupons.map((c) => ({
|
||||
couponId: c.couponId,
|
||||
quantity: c.quantity,
|
||||
}));
|
||||
}
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
return [];
|
||||
},
|
||||
normalizeCouponFaceFromDetail(item) {
|
||||
let face = this.formatCouponFace(item);
|
||||
if (face !== "-") return face;
|
||||
const raw = item.faceValueText;
|
||||
if (raw == null || raw === "") return "-";
|
||||
return String(raw)
|
||||
.replace(/^减免现金\s*/, "")
|
||||
.replace(/^减免\s*/, "")
|
||||
.trim() || "-";
|
||||
},
|
||||
mapCouponItemsToPackageRows(items) {
|
||||
if (!Array.isArray(items)) return [];
|
||||
return items.map((item) => {
|
||||
const face = this.normalizeCouponFaceFromDetail(item);
|
||||
const validHtml = item.validityText
|
||||
? String(item.validityText).replace(/\n/g, "<br/>")
|
||||
: "-";
|
||||
return {
|
||||
couponId: item.couponId,
|
||||
quantity: Math.min(10, Math.max(1, Number(item.quantity) || 1)),
|
||||
couponName: item.couponName || "",
|
||||
faceValueLabel: face,
|
||||
validRange: validHtml,
|
||||
};
|
||||
});
|
||||
},
|
||||
async hydrateCouponRowsForEdit(snippets) {
|
||||
const rows = [];
|
||||
for (const s of snippets) {
|
||||
const cid = s.couponId;
|
||||
const qty = s.quantity;
|
||||
try {
|
||||
const res = await getPlatformCoupon(cid);
|
||||
if (res && res.success && res.result) {
|
||||
rows.push(this.buildCouponDisplayRow(res.result, qty));
|
||||
} else {
|
||||
rows.push({
|
||||
couponId: cid,
|
||||
quantity: Math.min(10, Math.max(1, Number(qty) || 1)),
|
||||
couponName: String(cid),
|
||||
faceValueLabel: "-",
|
||||
validRange: "-",
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
rows.push({
|
||||
couponId: cid,
|
||||
quantity: Math.min(10, Math.max(1, Number(qty) || 1)),
|
||||
couponName: String(cid),
|
||||
faceValueLabel: "-",
|
||||
validRange: "-",
|
||||
});
|
||||
}
|
||||
}
|
||||
this.formEdit.couponPackageRows = rows;
|
||||
},
|
||||
handleCouponPickerClose() {
|
||||
this.couponPickerVisible = false;
|
||||
},
|
||||
openCouponPicker(which) {
|
||||
this.couponPickerWhich = which;
|
||||
this.couponPickerVisible = true;
|
||||
},
|
||||
confirmCouponPicker() {
|
||||
const list = this.$refs.couponPicker?.getSelection?.() || [];
|
||||
if (!list.length) {
|
||||
ElMessage.warning("请至少选择一张优惠券");
|
||||
return;
|
||||
}
|
||||
this.onCouponTemplateSelected(list);
|
||||
this.couponPickerVisible = false;
|
||||
},
|
||||
onCouponTemplateSelected(selectedRows) {
|
||||
const which = this.couponPickerWhich;
|
||||
const form = which === "add" ? this.formAdd : this.formEdit;
|
||||
const refName = which === "add" ? "addForm" : "editForm";
|
||||
const list = selectedRows || [];
|
||||
const rows = list.map((row) => {
|
||||
const existing = (form.couponPackageRows || []).find((r) => String(r.couponId) === String(row.id));
|
||||
const qty = existing ? existing.quantity : 1;
|
||||
return this.buildCouponDisplayRow(row, qty);
|
||||
});
|
||||
form.couponPackageRows = rows;
|
||||
this.touchFormField(refName, "couponPackageRows");
|
||||
},
|
||||
removeCouponRow(which, index) {
|
||||
const form = which === "add" ? this.formAdd : this.formEdit;
|
||||
const refName = which === "add" ? "addForm" : "editForm";
|
||||
form.couponPackageRows.splice(index, 1);
|
||||
this.touchFormField(refName, "couponPackageRows");
|
||||
},
|
||||
init() {
|
||||
this.loadBenefitTypes();
|
||||
this.getData();
|
||||
},
|
||||
loadBenefitTypes() {
|
||||
API_Member.getMemberBenefitTypes().then((res) => {
|
||||
if (res && res.success && Array.isArray(res.result)) {
|
||||
this.benefitTypeOptions = res.result;
|
||||
}).catch(() => {
|
||||
submitEditLoading.value = false;
|
||||
ElMessage.error("网络异常,请稍后重试");
|
||||
});
|
||||
});
|
||||
}
|
||||
function onBenefitStateSwitch(row, checked) {
|
||||
const nextState = checked ? "OPEN" : "CLOSE";
|
||||
const prevState = row.benefitState;
|
||||
if (nextState === prevState) return;
|
||||
const text = checked ? "开启" : "关闭";
|
||||
ElMessageBox.confirm(`确定${text}该客户权益?`, "提示", { type: "warning" }).then(() => {
|
||||
row._benefitStateLoading = true;
|
||||
return API_Member.updateMemberBenefitState(row.id, nextState)
|
||||
.then((res) => {
|
||||
row._benefitStateLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success(`${text}成功`);
|
||||
row.benefitState = nextState;
|
||||
} else {
|
||||
this.benefitTypeOptions = [];
|
||||
getData();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
row._benefitStateLoading = false;
|
||||
});
|
||||
},
|
||||
changePage(page) {
|
||||
this.searchForm.pageNumber = page;
|
||||
this.getData();
|
||||
},
|
||||
changePageSize(size) {
|
||||
this.searchForm.pageSize = size;
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getData();
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
const params = { ...this.searchForm };
|
||||
API_Member.getMemberBenefitByPage(params).then((res) => {
|
||||
this.loading = false;
|
||||
if (res && res.success && res.result) {
|
||||
this.data = Array.isArray(res.result.records) ? res.result.records : [];
|
||||
this.total = Number(res.result.total) || 0;
|
||||
} else {
|
||||
this.data = [];
|
||||
this.total = 0;
|
||||
}
|
||||
});
|
||||
},
|
||||
openAdd() {
|
||||
this.addFlag = true;
|
||||
this.submitAddLoading = false;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.addForm) this.$refs.addForm.resetFields();
|
||||
this.formAdd = buildDefaultForm();
|
||||
});
|
||||
},
|
||||
submitAdd() {
|
||||
if (!this.$refs.addForm) {
|
||||
ElMessage.warning("表单未就绪,请稍后重试");
|
||||
return;
|
||||
}).catch(() => {});
|
||||
}
|
||||
function remove(row) {
|
||||
ElMessageBox.confirm("确定删除该客户权益?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.deleteMemberBenefit(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
this.$refs.addForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitAddLoading = true;
|
||||
const { giftPoint, couponPackageRows, ...rest } = this.formAdd;
|
||||
const payload = {
|
||||
...rest,
|
||||
benefitConfig: this.buildBenefitConfigString({
|
||||
benefitType: this.formAdd.benefitType,
|
||||
giftPoint,
|
||||
couponPackageRows,
|
||||
}),
|
||||
};
|
||||
API_Member.addMemberBenefit(payload).then((res) => {
|
||||
this.submitAddLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
this.addFlag = false;
|
||||
this.getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
} else {
|
||||
ElMessage.error("添加失败");
|
||||
}
|
||||
}).catch(() => {
|
||||
this.submitAddLoading = false;
|
||||
ElMessage.error("网络异常,请稍后重试");
|
||||
});
|
||||
});
|
||||
},
|
||||
openEdit(row) {
|
||||
this.editFlag = true;
|
||||
this.submitEditLoading = false;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.editForm) this.$refs.editForm.resetFields();
|
||||
});
|
||||
API_Member.getMemberBenefit(row.id).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
const raw = res.result;
|
||||
const detail = raw.benefit != null ? raw.benefit : raw;
|
||||
const couponItems = raw.couponItems;
|
||||
const bt = detail.benefitType || "";
|
||||
const parsed = this.parseGiftPointFromConfig(bt, detail.benefitConfig || "");
|
||||
const couponSnippets = this.parseCouponsFromConfig(detail.benefitConfig || "");
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
let couponRows = [];
|
||||
if (bt === COUPON_PACKAGE && Array.isArray(couponItems) && couponItems.length) {
|
||||
couponRows = this.mapCouponItemsToPackageRows(couponItems);
|
||||
}
|
||||
|
||||
this.formEdit = {
|
||||
id: detail.id || row.id || "",
|
||||
benefitName: detail.benefitName || "",
|
||||
benefitCode: detail.benefitCode || "",
|
||||
benefitType: bt,
|
||||
benefitLogo: detail.benefitLogo || "",
|
||||
benefitDesc: detail.benefitDesc || "",
|
||||
benefitSort: Number(detail.benefitSort) > 0 ? Number(detail.benefitSort) : 1,
|
||||
benefitState: detail.benefitState || "OPEN",
|
||||
benefitConfig: detail.benefitConfig || "",
|
||||
giftPoint: bt === GIFT_POINT ? (parsed !== null ? parsed : null) : null,
|
||||
couponPackageRows: couponRows,
|
||||
};
|
||||
|
||||
if (bt === COUPON_PACKAGE && couponRows.length === 0 && couponSnippets.length) {
|
||||
this.hydrateCouponRowsForEdit(couponSnippets);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
submitEdit() {
|
||||
if (!this.$refs.editForm) {
|
||||
ElMessage.warning("表单未就绪,请稍后重试");
|
||||
return;
|
||||
}
|
||||
this.$refs.editForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitEditLoading = true;
|
||||
const { id, giftPoint, couponPackageRows, ...rest } = this.formEdit;
|
||||
const payload = {
|
||||
...rest,
|
||||
benefitConfig: this.buildBenefitConfigString({
|
||||
benefitType: this.formEdit.benefitType,
|
||||
giftPoint,
|
||||
couponPackageRows,
|
||||
}),
|
||||
};
|
||||
API_Member.updateMemberBenefit(id, payload).then((res) => {
|
||||
this.submitEditLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
this.editFlag = false;
|
||||
this.getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
} else {
|
||||
ElMessage.error("修改失败");
|
||||
}
|
||||
}).catch(() => {
|
||||
this.submitEditLoading = false;
|
||||
ElMessage.error("网络异常,请稍后重试");
|
||||
});
|
||||
});
|
||||
},
|
||||
onBenefitStateSwitch(row, checked) {
|
||||
const nextState = checked ? "OPEN" : "CLOSE";
|
||||
const prevState = row.benefitState;
|
||||
if (nextState === prevState) return;
|
||||
const text = checked ? "开启" : "关闭";
|
||||
ElMessageBox.confirm(`确定${text}该客户权益?`, "提示", { type: "warning" }).then(() => {
|
||||
row._benefitStateLoading = true;
|
||||
return API_Member.updateMemberBenefitState(row.id, nextState)
|
||||
.then((res) => {
|
||||
row._benefitStateLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success(`${text}成功`);
|
||||
row.benefitState = nextState;
|
||||
} else {
|
||||
this.getData();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
row._benefitStateLoading = false;
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
remove(row) {
|
||||
ElMessageBox.confirm("确定删除该客户权益?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.deleteMemberBenefit(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
this.getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="90px"
|
||||
@@ -78,7 +78,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Member from "@/api/member";
|
||||
|
||||
const RULE_OPTIONS = [
|
||||
@@ -95,79 +96,76 @@ const RULE_OPTIONS = [
|
||||
{ value: "SHARE_BUY", label: "分享购买" },
|
||||
];
|
||||
|
||||
export default {
|
||||
name: "memberGradeExperienceLog",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
total: 0,
|
||||
data: [],
|
||||
ruleOptions: RULE_OPTIONS,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
memberMobile: "",
|
||||
ruleKey: "",
|
||||
},
|
||||
};
|
||||
},
|
||||
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 : "";
|
||||
},
|
||||
init() {
|
||||
this.getData();
|
||||
},
|
||||
buildParams() {
|
||||
const params = {
|
||||
pageNumber: this.searchForm.pageNumber,
|
||||
pageSize: this.searchForm.pageSize,
|
||||
};
|
||||
if (this.searchForm.memberMobile) params.mobile = this.searchForm.memberMobile;
|
||||
if (this.searchForm.ruleKey) params.ruleKey = this.searchForm.ruleKey;
|
||||
return params;
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
API_Member.getMemberExperienceByPage(this.buildParams())
|
||||
.then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
} else {
|
||||
this.data = [];
|
||||
this.total = 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getData();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getData();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getData();
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "memberGradeExperienceLog" });
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
const data = ref([]);
|
||||
const ruleOptions = RULE_OPTIONS;
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
memberMobile: "",
|
||||
ruleKey: "",
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
function formatExperienceValue(row) {
|
||||
const v = row.value ?? row.variableExperience ?? row.experience ?? row.variableValue;
|
||||
return v == null ? "-" : v;
|
||||
}
|
||||
function formatMaxValue(row) {
|
||||
const v = row.maxValue ?? row.maxExperience ?? row.limitValue;
|
||||
return v == null ? "-" : v;
|
||||
}
|
||||
function findRuleName(ruleKey) {
|
||||
const hit = ruleOptions.find((item) => item.value === ruleKey);
|
||||
return hit ? hit.label : "";
|
||||
}
|
||||
function init() {
|
||||
getData();
|
||||
}
|
||||
function buildParams() {
|
||||
const params = {
|
||||
pageNumber: searchForm.pageNumber,
|
||||
pageSize: searchForm.pageSize,
|
||||
};
|
||||
if (searchForm.memberMobile) params.mobile = searchForm.memberMobile;
|
||||
if (searchForm.ruleKey) params.ruleKey = searchForm.ruleKey;
|
||||
return params;
|
||||
}
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
API_Member.getMemberExperienceByPage(buildParams())
|
||||
.then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
} else {
|
||||
data.value = [];
|
||||
total.value = 0;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getData();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getData();
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -237,10 +237,13 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { getSetting, setSetting } from "@/api/index";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
defineOptions({ name: "memberGradeExperienceSetting" });
|
||||
|
||||
const RULE_OPTIONS = [
|
||||
{ ruleKey: "CONSUME", ruleName: "消费" },
|
||||
{ ruleKey: "REGISTER", ruleName: "注册" },
|
||||
@@ -268,176 +271,169 @@ const defaultForm = () => ({
|
||||
description: "",
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "memberGradeExperienceSetting",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
submitLoading: false,
|
||||
form: defaultForm(),
|
||||
const loading = ref(false);
|
||||
const submitLoading = ref(false);
|
||||
const form = ref(defaultForm());
|
||||
|
||||
function formatInputValue(v) {
|
||||
return v == null ? "" : String(v);
|
||||
}
|
||||
function getRawInputValue(v) {
|
||||
return v && v.target ? v.target.value : v;
|
||||
}
|
||||
function updateRuleEnabled(index, enabled) {
|
||||
const item = form.value.items[index] || {};
|
||||
form.value.items[index] = {
|
||||
...item,
|
||||
enabled: !!enabled,
|
||||
};
|
||||
}
|
||||
function updateRuleValue(index, v) {
|
||||
const raw = getRawInputValue(v);
|
||||
const next = Number(raw);
|
||||
const item = form.value.items[index] || {};
|
||||
form.value.items[index] = {
|
||||
...item,
|
||||
value: Number.isFinite(next) ? next : null,
|
||||
};
|
||||
}
|
||||
function commitRuleValue(index) {
|
||||
const item = form.value.items[index] || {};
|
||||
const next = Number(item.value);
|
||||
let value = 1;
|
||||
if (Number.isFinite(next)) {
|
||||
if (next < 1) value = 1;
|
||||
else if (next > 100) value = 100;
|
||||
else value = Math.floor(next);
|
||||
}
|
||||
form.value.items[index] = {
|
||||
...item,
|
||||
value,
|
||||
};
|
||||
}
|
||||
function updateRuleMaxValue(index, v) {
|
||||
const raw = getRawInputValue(v);
|
||||
const item = form.value.items[index] || {};
|
||||
if (raw == null || raw === "") {
|
||||
form.value.items[index] = {
|
||||
...item,
|
||||
maxValue: null,
|
||||
};
|
||||
},
|
||||
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.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.form.items[index] = {
|
||||
...item,
|
||||
value: Number.isFinite(next) ? next : null,
|
||||
};
|
||||
},
|
||||
commitRuleValue(index) {
|
||||
const item = this.form.items[index] || {};
|
||||
const next = Number(item.value);
|
||||
let value = 1;
|
||||
if (Number.isFinite(next)) {
|
||||
if (next < 1) value = 1;
|
||||
else if (next > 100) value = 100;
|
||||
else value = Math.floor(next);
|
||||
}
|
||||
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.form.items[index] = {
|
||||
...item,
|
||||
maxValue: null,
|
||||
};
|
||||
return;
|
||||
}
|
||||
const next = Number(raw);
|
||||
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.form.items[index] = {
|
||||
...item,
|
||||
maxValue: null,
|
||||
};
|
||||
return;
|
||||
}
|
||||
const next = Number(item.maxValue);
|
||||
this.form.items[index] = {
|
||||
...item,
|
||||
maxValue: Number.isFinite(next) && next >= 1 ? Math.floor(next) : null,
|
||||
};
|
||||
},
|
||||
normalizeConfig(val) {
|
||||
if (!val) return {};
|
||||
if (typeof val === "string") {
|
||||
try {
|
||||
return JSON.parse(val);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (typeof val === "object") return val;
|
||||
return;
|
||||
}
|
||||
const next = Number(raw);
|
||||
form.value.items[index] = {
|
||||
...item,
|
||||
maxValue: Number.isFinite(next) ? next : null,
|
||||
};
|
||||
}
|
||||
function commitRuleMaxValue(index) {
|
||||
const item = form.value.items[index] || {};
|
||||
if (item.maxValue == null || item.maxValue === "") {
|
||||
form.value.items[index] = {
|
||||
...item,
|
||||
maxValue: null,
|
||||
};
|
||||
return;
|
||||
}
|
||||
const next = Number(item.maxValue);
|
||||
form.value.items[index] = {
|
||||
...item,
|
||||
maxValue: Number.isFinite(next) && next >= 1 ? Math.floor(next) : null,
|
||||
};
|
||||
}
|
||||
function normalizeConfig(val) {
|
||||
if (!val) return {};
|
||||
if (typeof val === "string") {
|
||||
try {
|
||||
return JSON.parse(val);
|
||||
} catch (e) {
|
||||
return {};
|
||||
},
|
||||
normalizeItems(items) {
|
||||
const map = {};
|
||||
if (Array.isArray(items)) {
|
||||
items.forEach((item) => {
|
||||
if (!item || !item.ruleKey) return;
|
||||
map[item.ruleKey] = item;
|
||||
});
|
||||
}
|
||||
return RULE_OPTIONS.map((rule) => {
|
||||
const hit = map[rule.ruleKey] || {};
|
||||
return {
|
||||
ruleKey: rule.ruleKey,
|
||||
ruleName: hit.ruleName || rule.ruleName,
|
||||
enabled: !!hit.enabled,
|
||||
value: Number(hit.value) > 0 ? Number(hit.value) : 1,
|
||||
maxValue: hit.maxValue == null || hit.maxValue === "" ? null : Number(hit.maxValue),
|
||||
}
|
||||
}
|
||||
if (typeof val === "object") return val;
|
||||
return {};
|
||||
}
|
||||
function normalizeItems(items) {
|
||||
const map = {};
|
||||
if (Array.isArray(items)) {
|
||||
items.forEach((item) => {
|
||||
if (!item || !item.ruleKey) return;
|
||||
map[item.ruleKey] = item;
|
||||
});
|
||||
}
|
||||
return RULE_OPTIONS.map((rule) => {
|
||||
const hit = map[rule.ruleKey] || {};
|
||||
return {
|
||||
ruleKey: rule.ruleKey,
|
||||
ruleName: hit.ruleName || rule.ruleName,
|
||||
enabled: !!hit.enabled,
|
||||
value: Number(hit.value) > 0 ? Number(hit.value) : 1,
|
||||
maxValue: hit.maxValue == null || hit.maxValue === "" ? null : Number(hit.maxValue),
|
||||
};
|
||||
});
|
||||
}
|
||||
function loadData() {
|
||||
loading.value = true;
|
||||
getSetting("EXPERIENCE_SETTING")
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const cfg = normalizeConfig(res.result);
|
||||
form.value = {
|
||||
items: normalizeItems(cfg.items),
|
||||
description: cfg.description || "",
|
||||
};
|
||||
});
|
||||
},
|
||||
loadData() {
|
||||
this.loading = true;
|
||||
getSetting("EXPERIENCE_SETTING")
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
const cfg = this.normalizeConfig(res.result);
|
||||
this.form = {
|
||||
items: this.normalizeItems(cfg.items),
|
||||
description: cfg.description || "",
|
||||
};
|
||||
} else {
|
||||
this.form = defaultForm();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
validateForm() {
|
||||
const invalid = this.form.items.find((item) => {
|
||||
const v = Number(item.value);
|
||||
if (!Number.isInteger(v) || v < 1 || v > 100) return true;
|
||||
if (item.maxValue != null && item.maxValue !== "") {
|
||||
const m = Number(item.maxValue);
|
||||
if (!Number.isInteger(m) || m < 1) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (invalid) {
|
||||
ElMessage.error("请检查经验值配置,经验值范围为1-100,限额需为正整数");
|
||||
return false;
|
||||
} else {
|
||||
form.value = defaultForm();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
submit() {
|
||||
if (!this.validateForm()) return;
|
||||
const payload = {
|
||||
items: this.form.items.map((item) => ({
|
||||
ruleKey: item.ruleKey,
|
||||
ruleName: item.ruleName,
|
||||
enabled: !!item.enabled,
|
||||
value: Number(item.value),
|
||||
maxValue: item.maxValue == null || item.maxValue === "" ? null : Number(item.maxValue),
|
||||
})),
|
||||
description: this.form.description || "",
|
||||
};
|
||||
this.submitLoading = true;
|
||||
setSetting("EXPERIENCE_SETTING", payload)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.submitLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function validateForm() {
|
||||
const invalid = form.value.items.find((item) => {
|
||||
const v = Number(item.value);
|
||||
if (!Number.isInteger(v) || v < 1 || v > 100) return true;
|
||||
if (item.maxValue != null && item.maxValue !== "") {
|
||||
const m = Number(item.maxValue);
|
||||
if (!Number.isInteger(m) || m < 1) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (invalid) {
|
||||
ElMessage.error("请检查经验值配置,经验值范围为1-100,限额需为正整数");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function submit() {
|
||||
if (!validateForm()) return;
|
||||
const payload = {
|
||||
items: form.value.items.map((item) => ({
|
||||
ruleKey: item.ruleKey,
|
||||
ruleName: item.ruleName,
|
||||
enabled: !!item.enabled,
|
||||
value: Number(item.value),
|
||||
maxValue: item.maxValue == null || item.maxValue === "" ? null : Number(item.maxValue),
|
||||
})),
|
||||
description: form.value.description || "",
|
||||
};
|
||||
submitLoading.value = true;
|
||||
setSetting("EXPERIENCE_SETTING", payload)
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -178,11 +178,14 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick, onMounted } from "vue";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
import uploadPicInput from "@/components/lili/upload-pic-input";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
defineOptions({ name: "memberGrade" });
|
||||
|
||||
const buildDefaultForm = () => ({
|
||||
id: "",
|
||||
gradeName: "",
|
||||
@@ -209,254 +212,248 @@ function syncOrderedBenefitIds(prevOrder, selected) {
|
||||
return out;
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "memberGrade",
|
||||
components: {
|
||||
uploadPicInput,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
data: [],
|
||||
addFlag: false,
|
||||
editFlag: false,
|
||||
submitAddLoading: false,
|
||||
submitEditLoading: false,
|
||||
formAdd: buildDefaultForm(),
|
||||
formEdit: buildDefaultForm(),
|
||||
addBenefitOrder: [],
|
||||
editBenefitOrder: [],
|
||||
benefitOptions: [],
|
||||
benefitTypeOptions: [],
|
||||
benefitOptionsLoading: false,
|
||||
editDetailLoading: false,
|
||||
rules: {
|
||||
gradeName: [{ required: true, message: "请输入等级名称", trigger: "blur" }],
|
||||
gradeImage: [{ required: true, message: "请上传等级图标", trigger: "change" }],
|
||||
requiredExperience: [{ required: true, type: "number", message: "请输入所需经验值", trigger: "change" }],
|
||||
gradeSort: [{ required: true, type: "number", message: "请输入等级排序", trigger: "change" }],
|
||||
gradeState: [{ required: true, message: "请选择等级开关", trigger: "change" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
benefitOptionLabel(b) {
|
||||
if (!b) return "";
|
||||
const name = b.benefitName || String(b.id);
|
||||
const opt = this.benefitTypeOptions.find((o) => o.value === b.benefitType);
|
||||
const typeText = opt ? opt.description : b.benefitType || "";
|
||||
return typeText ? `${name}(${typeText})` : name;
|
||||
},
|
||||
onAddBenefitIdsChange(val) {
|
||||
this.addBenefitOrder = syncOrderedBenefitIds(this.addBenefitOrder, val);
|
||||
},
|
||||
onEditBenefitIdsChange(val) {
|
||||
this.editBenefitOrder = syncOrderedBenefitIds(this.editBenefitOrder, val);
|
||||
},
|
||||
loadBenefitTypes() {
|
||||
API_Member.getMemberBenefitTypes().then((res) => {
|
||||
if (res && res.success && Array.isArray(res.result)) {
|
||||
this.benefitTypeOptions = res.result;
|
||||
} else {
|
||||
this.benefitTypeOptions = [];
|
||||
}
|
||||
});
|
||||
},
|
||||
loadBenefitOptions() {
|
||||
this.benefitOptionsLoading = true;
|
||||
const pageSize = 500;
|
||||
const fetchPage = (pageNumber) =>
|
||||
API_Member.getMemberBenefitByPage({ pageNumber, pageSize, sort: "benefitSort", order: "asc" });
|
||||
return fetchPage(1)
|
||||
.then((res) => {
|
||||
if (!(res && res.success && res.result)) {
|
||||
this.benefitOptions = [];
|
||||
return;
|
||||
}
|
||||
const records = Array.isArray(res.result.records) ? res.result.records : [];
|
||||
const total = Number(res.result.total) || records.length;
|
||||
let all = records.slice();
|
||||
if (total > pageSize) {
|
||||
const pages = Math.ceil(total / pageSize);
|
||||
const rest = [];
|
||||
for (let p = 2; p <= pages; p++) {
|
||||
rest.push(fetchPage(p));
|
||||
}
|
||||
return Promise.all(rest).then((results) => {
|
||||
results.forEach((r) => {
|
||||
if (r && r.success && r.result && Array.isArray(r.result.records)) {
|
||||
all = all.concat(r.result.records);
|
||||
}
|
||||
});
|
||||
this.benefitOptions = all;
|
||||
});
|
||||
}
|
||||
this.benefitOptions = all;
|
||||
})
|
||||
.catch(() => {
|
||||
this.benefitOptions = [];
|
||||
})
|
||||
.finally(() => {
|
||||
this.benefitOptionsLoading = false;
|
||||
});
|
||||
},
|
||||
parseBenefitIdsFromString(str) {
|
||||
return String(str || "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
},
|
||||
fillEditFormFromGrade(grade, benefitsOrderedIds) {
|
||||
this.formEdit = {
|
||||
id: grade.id || "",
|
||||
gradeName: grade.gradeName || "",
|
||||
isDefault: grade.isDefault === true,
|
||||
gradeImage: grade.gradeImage || "",
|
||||
gradeBackground: grade.gradeBackground || "",
|
||||
gradeFontColor: grade.gradeFontColor || "",
|
||||
requiredExperience: Number(grade.requiredExperience) > 0 ? Number(grade.requiredExperience) : 1,
|
||||
gradeSort: Number(grade.gradeSort) > 0 ? Number(grade.gradeSort) : 1,
|
||||
gradeState: grade.gradeState || "OPEN",
|
||||
benefitIds: grade.benefitIds || "",
|
||||
};
|
||||
this.editBenefitOrder = (benefitsOrderedIds || []).map((id) => String(id));
|
||||
},
|
||||
init() {
|
||||
this.getData();
|
||||
this.loadBenefitTypes();
|
||||
this.loadBenefitOptions();
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
API_Member.getMemberGradeByPage().then((res) => {
|
||||
this.loading = false;
|
||||
if (res && res.success) {
|
||||
this.data = Array.isArray(res.result) ? res.result : [];
|
||||
}
|
||||
});
|
||||
},
|
||||
openAdd() {
|
||||
this.addFlag = true;
|
||||
this.submitAddLoading = false;
|
||||
if (!this.benefitOptions.length && !this.benefitOptionsLoading) {
|
||||
this.loadBenefitOptions();
|
||||
// 模板 el-form 的 ref(ref="addForm" / ref="editForm")
|
||||
const addForm = ref(null);
|
||||
const editForm = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const data = ref([]);
|
||||
const addFlag = ref(false);
|
||||
const editFlag = ref(false);
|
||||
const submitAddLoading = ref(false);
|
||||
const submitEditLoading = ref(false);
|
||||
const formAdd = ref(buildDefaultForm());
|
||||
const formEdit = ref(buildDefaultForm());
|
||||
const addBenefitOrder = ref([]);
|
||||
const editBenefitOrder = ref([]);
|
||||
const benefitOptions = ref([]);
|
||||
const benefitTypeOptions = ref([]);
|
||||
const benefitOptionsLoading = ref(false);
|
||||
const editDetailLoading = ref(false);
|
||||
const rules = ref({
|
||||
gradeName: [{ required: true, message: "请输入等级名称", trigger: "blur" }],
|
||||
gradeImage: [{ required: true, message: "请上传等级图标", trigger: "change" }],
|
||||
requiredExperience: [{ required: true, type: "number", message: "请输入所需经验值", trigger: "change" }],
|
||||
gradeSort: [{ required: true, type: "number", message: "请输入等级排序", trigger: "change" }],
|
||||
gradeState: [{ required: true, message: "请选择等级开关", trigger: "change" }],
|
||||
});
|
||||
|
||||
function benefitOptionLabel(b) {
|
||||
if (!b) return "";
|
||||
const name = b.benefitName || String(b.id);
|
||||
const opt = benefitTypeOptions.value.find((o) => o.value === b.benefitType);
|
||||
const typeText = opt ? opt.description : b.benefitType || "";
|
||||
return typeText ? `${name}(${typeText})` : name;
|
||||
}
|
||||
function onAddBenefitIdsChange(val) {
|
||||
addBenefitOrder.value = syncOrderedBenefitIds(addBenefitOrder.value, val);
|
||||
}
|
||||
function onEditBenefitIdsChange(val) {
|
||||
editBenefitOrder.value = syncOrderedBenefitIds(editBenefitOrder.value, val);
|
||||
}
|
||||
function loadBenefitTypes() {
|
||||
API_Member.getMemberBenefitTypes().then((res) => {
|
||||
if (res && res.success && Array.isArray(res.result)) {
|
||||
benefitTypeOptions.value = res.result;
|
||||
} else {
|
||||
benefitTypeOptions.value = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
function loadBenefitOptions() {
|
||||
benefitOptionsLoading.value = true;
|
||||
const pageSize = 500;
|
||||
const fetchPage = (pageNumber) =>
|
||||
API_Member.getMemberBenefitByPage({ pageNumber, pageSize, sort: "benefitSort", order: "asc" });
|
||||
return fetchPage(1)
|
||||
.then((res) => {
|
||||
if (!(res && res.success && res.result)) {
|
||||
benefitOptions.value = [];
|
||||
return;
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.addForm) this.$refs.addForm.resetFields();
|
||||
this.formAdd = buildDefaultForm();
|
||||
this.addBenefitOrder = [];
|
||||
});
|
||||
},
|
||||
submitAdd() {
|
||||
this.$refs.addForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitAddLoading = true;
|
||||
const { benefitIds: _omit, ...rest } = this.formAdd;
|
||||
const payload = {
|
||||
...rest,
|
||||
benefitIds: (this.addBenefitOrder || []).join(","),
|
||||
};
|
||||
API_Member.addMemberGrade(payload).then((res) => {
|
||||
this.submitAddLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
this.addFlag = false;
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
openEdit(row) {
|
||||
this.editFlag = true;
|
||||
this.submitEditLoading = false;
|
||||
this.editDetailLoading = true;
|
||||
if (!this.benefitOptions.length && !this.benefitOptionsLoading) {
|
||||
this.loadBenefitOptions();
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.editForm) this.$refs.editForm.resetFields();
|
||||
});
|
||||
API_Member.getMemberGrade(row.id)
|
||||
.then((res) => {
|
||||
this.editDetailLoading = false;
|
||||
if (res && res.success && res.result) {
|
||||
const raw = res.result;
|
||||
const grade = raw.grade != null ? raw.grade : raw;
|
||||
const benefits = Array.isArray(raw.benefits) ? raw.benefits : [];
|
||||
const orderedIds = benefits.length
|
||||
? benefits.map((b) => b.id).filter((id) => id != null && id !== "")
|
||||
: String(grade.benefitIds || "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
this.fillEditFormFromGrade(grade, orderedIds);
|
||||
} else {
|
||||
this.fillEditFormFromGrade(row, this.parseBenefitIdsFromString(row.benefitIds));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.editDetailLoading = false;
|
||||
this.fillEditFormFromGrade(row, this.parseBenefitIdsFromString(row.benefitIds));
|
||||
});
|
||||
},
|
||||
submitEdit() {
|
||||
this.$refs.editForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitEditLoading = true;
|
||||
const { id, benefitIds: _omit, ...rest } = this.formEdit;
|
||||
const payload = {
|
||||
...rest,
|
||||
benefitIds: (this.editBenefitOrder || []).join(","),
|
||||
};
|
||||
API_Member.updateMemberGrade(id, payload).then((res) => {
|
||||
this.submitEditLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
this.editFlag = false;
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
onGradeStateSwitch(row, checked) {
|
||||
const nextState = checked ? "OPEN" : "CLOSE";
|
||||
const prevState = row.gradeState;
|
||||
if (nextState === prevState) return;
|
||||
const text = checked ? "开启" : "关闭";
|
||||
ElMessageBox.confirm(`确定${text}该客户等级?`, "提示", { type: "warning" }).then(() => {
|
||||
row._gradeStateLoading = true;
|
||||
return API_Member.updateMemberGradeState(row.id, nextState)
|
||||
.then((res) => {
|
||||
row._gradeStateLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success(`${text}成功`);
|
||||
row.gradeState = nextState;
|
||||
} else {
|
||||
this.getData();
|
||||
const records = Array.isArray(res.result.records) ? res.result.records : [];
|
||||
const total = Number(res.result.total) || records.length;
|
||||
let all = records.slice();
|
||||
if (total > pageSize) {
|
||||
const pages = Math.ceil(total / pageSize);
|
||||
const rest = [];
|
||||
for (let p = 2; p <= pages; p++) {
|
||||
rest.push(fetchPage(p));
|
||||
}
|
||||
return Promise.all(rest).then((results) => {
|
||||
results.forEach((r) => {
|
||||
if (r && r.success && r.result && Array.isArray(r.result.records)) {
|
||||
all = all.concat(r.result.records);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
row._gradeStateLoading = false;
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
remove(row) {
|
||||
ElMessageBox.confirm("确定删除该客户等级?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.deleteMemberGrade(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
this.getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
benefitOptions.value = all;
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
}
|
||||
benefitOptions.value = all;
|
||||
})
|
||||
.catch(() => {
|
||||
benefitOptions.value = [];
|
||||
})
|
||||
.finally(() => {
|
||||
benefitOptionsLoading.value = false;
|
||||
});
|
||||
}
|
||||
function parseBenefitIdsFromString(str) {
|
||||
return String(str || "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
function fillEditFormFromGrade(grade, benefitsOrderedIds) {
|
||||
formEdit.value = {
|
||||
id: grade.id || "",
|
||||
gradeName: grade.gradeName || "",
|
||||
isDefault: grade.isDefault === true,
|
||||
gradeImage: grade.gradeImage || "",
|
||||
gradeBackground: grade.gradeBackground || "",
|
||||
gradeFontColor: grade.gradeFontColor || "",
|
||||
requiredExperience: Number(grade.requiredExperience) > 0 ? Number(grade.requiredExperience) : 1,
|
||||
gradeSort: Number(grade.gradeSort) > 0 ? Number(grade.gradeSort) : 1,
|
||||
gradeState: grade.gradeState || "OPEN",
|
||||
benefitIds: grade.benefitIds || "",
|
||||
};
|
||||
editBenefitOrder.value = (benefitsOrderedIds || []).map((id) => String(id));
|
||||
}
|
||||
function init() {
|
||||
getData();
|
||||
loadBenefitTypes();
|
||||
loadBenefitOptions();
|
||||
}
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
API_Member.getMemberGradeByPage().then((res) => {
|
||||
loading.value = false;
|
||||
if (res && res.success) {
|
||||
data.value = Array.isArray(res.result) ? res.result : [];
|
||||
}
|
||||
});
|
||||
}
|
||||
function openAdd() {
|
||||
addFlag.value = true;
|
||||
submitAddLoading.value = false;
|
||||
if (!benefitOptions.value.length && !benefitOptionsLoading.value) {
|
||||
loadBenefitOptions();
|
||||
}
|
||||
nextTick(() => {
|
||||
if (addForm.value) addForm.value.resetFields();
|
||||
formAdd.value = buildDefaultForm();
|
||||
addBenefitOrder.value = [];
|
||||
});
|
||||
}
|
||||
function submitAdd() {
|
||||
addForm.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitAddLoading.value = true;
|
||||
const { benefitIds: _omit, ...rest } = formAdd.value;
|
||||
const payload = {
|
||||
...rest,
|
||||
benefitIds: (addBenefitOrder.value || []).join(","),
|
||||
};
|
||||
API_Member.addMemberGrade(payload).then((res) => {
|
||||
submitAddLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
addFlag.value = false;
|
||||
getData();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function openEdit(row) {
|
||||
editFlag.value = true;
|
||||
submitEditLoading.value = false;
|
||||
editDetailLoading.value = true;
|
||||
if (!benefitOptions.value.length && !benefitOptionsLoading.value) {
|
||||
loadBenefitOptions();
|
||||
}
|
||||
nextTick(() => {
|
||||
if (editForm.value) editForm.value.resetFields();
|
||||
});
|
||||
API_Member.getMemberGrade(row.id)
|
||||
.then((res) => {
|
||||
editDetailLoading.value = false;
|
||||
if (res && res.success && res.result) {
|
||||
const raw = res.result;
|
||||
const grade = raw.grade != null ? raw.grade : raw;
|
||||
const benefits = Array.isArray(raw.benefits) ? raw.benefits : [];
|
||||
const orderedIds = benefits.length
|
||||
? benefits.map((b) => b.id).filter((id) => id != null && id !== "")
|
||||
: String(grade.benefitIds || "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
fillEditFormFromGrade(grade, orderedIds);
|
||||
} else {
|
||||
fillEditFormFromGrade(row, parseBenefitIdsFromString(row.benefitIds));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
editDetailLoading.value = false;
|
||||
fillEditFormFromGrade(row, parseBenefitIdsFromString(row.benefitIds));
|
||||
});
|
||||
}
|
||||
function submitEdit() {
|
||||
editForm.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitEditLoading.value = true;
|
||||
const { id, benefitIds: _omit, ...rest } = formEdit.value;
|
||||
const payload = {
|
||||
...rest,
|
||||
benefitIds: (editBenefitOrder.value || []).join(","),
|
||||
};
|
||||
API_Member.updateMemberGrade(id, payload).then((res) => {
|
||||
submitEditLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
editFlag.value = false;
|
||||
getData();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function onGradeStateSwitch(row, checked) {
|
||||
const nextState = checked ? "OPEN" : "CLOSE";
|
||||
const prevState = row.gradeState;
|
||||
if (nextState === prevState) return;
|
||||
const text = checked ? "开启" : "关闭";
|
||||
ElMessageBox.confirm(`确定${text}该客户等级?`, "提示", { type: "warning" }).then(() => {
|
||||
row._gradeStateLoading = true;
|
||||
return API_Member.updateMemberGradeState(row.id, nextState)
|
||||
.then((res) => {
|
||||
row._gradeStateLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success(`${text}成功`);
|
||||
row.gradeState = nextState;
|
||||
} else {
|
||||
getData();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
row._gradeStateLoading = false;
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
function remove(row) {
|
||||
ElMessageBox.confirm("确定删除该客户等级?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.deleteMemberGrade(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.ops a {
|
||||
|
||||
@@ -70,137 +70,136 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { nextTick, onMounted, reactive, ref } from "vue";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "memberGroup",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
addFlag: false,
|
||||
editFlag: false,
|
||||
submitAddLoading: false,
|
||||
submitEditLoading: false,
|
||||
formAdd: {
|
||||
groupName: "",
|
||||
description: "",
|
||||
},
|
||||
formEdit: {
|
||||
id: "",
|
||||
groupName: "",
|
||||
description: "",
|
||||
},
|
||||
rulesAdd: {
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
},
|
||||
rulesEdit: {
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getData();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getData();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getData();
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
API_Member.getMemberGroupByPage(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res && res.success && res.result) {
|
||||
this.data = res.result.records || [];
|
||||
this.total = res.result.total || 0;
|
||||
}
|
||||
});
|
||||
},
|
||||
openAdd() {
|
||||
this.addFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.addForm) this.$refs.addForm.resetFields();
|
||||
this.formAdd = { groupName: "", description: "" };
|
||||
});
|
||||
},
|
||||
submitAdd() {
|
||||
this.$refs.addForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitAddLoading = true;
|
||||
API_Member.addMemberGroup(this.formAdd).then((res) => {
|
||||
this.submitAddLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
this.addFlag = false;
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
openEdit(row) {
|
||||
this.editFlag = true;
|
||||
this.submitEditLoading = false;
|
||||
API_Member.getMemberGroup(row.id).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
this.formEdit = {
|
||||
id: res.result.id,
|
||||
groupName: res.result.groupName || "",
|
||||
description: res.result.description || "",
|
||||
};
|
||||
} else {
|
||||
this.formEdit = {
|
||||
id: row.id,
|
||||
groupName: row.groupName || "",
|
||||
description: row.description || "",
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
submitEdit() {
|
||||
this.$refs.editForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.submitEditLoading = true;
|
||||
const { id, groupName, description } = this.formEdit;
|
||||
API_Member.updateMemberGroup(id, { groupName, description }).then((res) => {
|
||||
this.submitEditLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
this.editFlag = false;
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
remove(row) {
|
||||
ElMessageBox.confirm("确定删除该分组?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.deleteMemberGroup(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
this.getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "memberGroup" });
|
||||
|
||||
const addForm = ref(null);
|
||||
const editForm = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const addFlag = ref(false);
|
||||
const editFlag = ref(false);
|
||||
const submitAddLoading = ref(false);
|
||||
const submitEditLoading = ref(false);
|
||||
const formAdd = ref({
|
||||
groupName: "",
|
||||
description: "",
|
||||
});
|
||||
const formEdit = ref({
|
||||
id: "",
|
||||
groupName: "",
|
||||
description: "",
|
||||
});
|
||||
const rulesAdd = reactive({
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
});
|
||||
const rulesEdit = reactive({
|
||||
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
|
||||
});
|
||||
|
||||
function init() {
|
||||
getData();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getData();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getData();
|
||||
}
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
API_Member.getMemberGroupByPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res && res.success && res.result) {
|
||||
data.value = res.result.records || [];
|
||||
total.value = res.result.total || 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
function openAdd() {
|
||||
addFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (addForm.value) addForm.value.resetFields();
|
||||
formAdd.value = { groupName: "", description: "" };
|
||||
});
|
||||
}
|
||||
function submitAdd() {
|
||||
addForm.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitAddLoading.value = true;
|
||||
API_Member.addMemberGroup(formAdd.value).then((res) => {
|
||||
submitAddLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
addFlag.value = false;
|
||||
getData();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function openEdit(row) {
|
||||
editFlag.value = true;
|
||||
submitEditLoading.value = false;
|
||||
API_Member.getMemberGroup(row.id).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
formEdit.value = {
|
||||
id: res.result.id,
|
||||
groupName: res.result.groupName || "",
|
||||
description: res.result.description || "",
|
||||
};
|
||||
} else {
|
||||
formEdit.value = {
|
||||
id: row.id,
|
||||
groupName: row.groupName || "",
|
||||
description: row.description || "",
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
function submitEdit() {
|
||||
editForm.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
submitEditLoading.value = true;
|
||||
const { id, groupName, description } = formEdit.value;
|
||||
API_Member.updateMemberGroup(id, { groupName, description }).then((res) => {
|
||||
submitEditLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
editFlag.value = false;
|
||||
getData();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
function remove(row) {
|
||||
ElMessageBox.confirm("确定删除该分组?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.deleteMemberGroup(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
getData();
|
||||
} else if (res && res.message) {
|
||||
ElMessage.error(res.message);
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -168,7 +168,7 @@
|
||||
|
||||
<el-dialog v-model="addFlag" title="添加会员" width="500px" destroy-on-close class="add-member-dialog">
|
||||
<el-form
|
||||
ref="addMemberForm"
|
||||
ref="addMemberFormRef"
|
||||
:model="addMemberForm"
|
||||
:rules="addRule"
|
||||
label-width="100px"
|
||||
@@ -210,7 +210,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="descFlag" :title="descTitle" width="500px" destroy-on-close>
|
||||
<el-form ref="form" :model="form" :rules="ruleValidate" label-width="80px">
|
||||
<el-form ref="formRef" :model="form" :rules="ruleValidate" label-width="80px">
|
||||
<el-input v-model="form.id" style="display: none" />
|
||||
<el-form-item label="头像">
|
||||
<img :src="form.face || defaultPic" class="face" />
|
||||
@@ -251,7 +251,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="walletIncreaseFlag" title="增加余额" width="420px" destroy-on-close>
|
||||
<el-form ref="walletIncreaseForm" :model="walletIncreaseForm" :rules="walletIncreaseRule" label-width="90px">
|
||||
<el-form ref="walletIncreaseFormRef" :model="walletIncreaseForm" :rules="walletIncreaseRule" label-width="90px">
|
||||
<el-form-item label="充值金额" prop="rechargeMoney">
|
||||
<el-input-number v-model="walletIncreaseForm.rechargeMoney" :min="0.01" :precision="2" style="width: 240px" />
|
||||
</el-form-item>
|
||||
@@ -263,7 +263,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="memberPointFlag" title="修改积分" width="420px" destroy-on-close>
|
||||
<el-form ref="memberPointForm" :model="memberPointForm" :rules="memberPointRule" label-width="90px">
|
||||
<el-form ref="memberPointFormRef" :model="memberPointForm" :rules="memberPointRule" label-width="90px">
|
||||
<el-form-item label="类型" prop="type">
|
||||
<el-radio-group v-model="memberPointForm.type">
|
||||
<el-radio-button value="INCREASE">增加</el-radio-button>
|
||||
@@ -281,7 +281,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="memberGroupFlag" title="设定会员分组" width="420px" destroy-on-close>
|
||||
<el-form ref="memberGroupForm" :model="memberGroupForm" :rules="memberGroupRule" label-width="90px">
|
||||
<el-form ref="memberGroupFormRef" :model="memberGroupForm" :rules="memberGroupRule" label-width="90px">
|
||||
<el-form-item label="会员分组" prop="groupId">
|
||||
<el-select v-model="memberGroupForm.groupId" clearable filterable style="width: 240px">
|
||||
<el-option
|
||||
@@ -300,13 +300,14 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="picModelFlag" width="1200px">
|
||||
<ossManage ref="ossManage" @callback="callbackSelected" :isComponent="true" :initialize="picModelFlag" />
|
||||
<ossManage ref="ossManageRef" @callback="callbackSelected" :isComponent="true" :initialize="picModelFlag" />
|
||||
</el-dialog>
|
||||
<multipleMap ref="map" @callback="selectedRegion" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, watch, nextTick, onMounted } from "vue";
|
||||
import { ArrowDown } from "@element-plus/icons-vue";
|
||||
import multipleMap from "@/components/map/multiple-map";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
@@ -314,451 +315,456 @@ import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
import * as RegExp from "@/libs/RegExp.js";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
import { md5 } from "@/utils/md5.js";
|
||||
import defaultPic from "@/assets/default.png";
|
||||
|
||||
export default {
|
||||
name: "member",
|
||||
components: {
|
||||
multipleMap,
|
||||
ossManage,
|
||||
ArrowDown,
|
||||
defineOptions({ name: "member" });
|
||||
|
||||
const props = defineProps({
|
||||
selectedMember: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
props: {
|
||||
selectedMember: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
selectedList: {
|
||||
type: null,
|
||||
default: () => [],
|
||||
},
|
||||
checkboxSelect: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
selectedList: {
|
||||
type: null,
|
||||
default: () => [],
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultPic,
|
||||
descTitle: "",
|
||||
descFlag: false,
|
||||
loading: true,
|
||||
addFlag: false,
|
||||
addMemberForm: {
|
||||
mobile: "",
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
order: "desc",
|
||||
id: "",
|
||||
username: "",
|
||||
mobile: "",
|
||||
disabled: "OPEN",
|
||||
groupId: "",
|
||||
},
|
||||
picModelFlag: false,
|
||||
form: {},
|
||||
walletIncreaseFlag: false,
|
||||
walletIncreaseLoading: false,
|
||||
walletIncreaseForm: {
|
||||
memberId: "",
|
||||
rechargeMoney: null,
|
||||
},
|
||||
walletIncreaseRule: {
|
||||
rechargeMoney: [
|
||||
{ required: true, type: "number", message: "请输入充值金额", trigger: "change" },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (typeof value !== "number" || value <= 0) {
|
||||
callback(new Error("充值金额必须大于0"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
},
|
||||
memberPointFlag: false,
|
||||
memberPointLoading: false,
|
||||
memberPointForm: {
|
||||
memberId: "",
|
||||
point: null,
|
||||
type: "INCREASE",
|
||||
},
|
||||
selectedRows: [],
|
||||
memberGroupFlag: false,
|
||||
memberGroupLoading: false,
|
||||
memberGroupForm: {
|
||||
groupId: "",
|
||||
},
|
||||
memberGroupRule: {
|
||||
groupId: [{ required: true, message: "请选择会员分组", trigger: "change" }],
|
||||
},
|
||||
memberGroupList: [],
|
||||
memberGradeList: [],
|
||||
memberPointRule: {
|
||||
type: [{ required: true, message: "请选择类型", trigger: "change" }],
|
||||
point: [
|
||||
{ required: true, type: "number", message: "请输入积分", trigger: "change" },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (typeof value !== "number" || value <= 0) {
|
||||
callback(new Error("积分必须大于0"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
},
|
||||
addRule: {
|
||||
mobile: [
|
||||
{ required: true, message: "请输入手机号码" },
|
||||
{ pattern: RegExp.mobile, message: "请输入正确的手机号" },
|
||||
],
|
||||
username: [{ required: true, message: "请输入客户名称" }],
|
||||
password: [{ required: true, message: "请输入密码" }],
|
||||
},
|
||||
ruleValidate: {},
|
||||
data: [],
|
||||
total: 0,
|
||||
selectMember: [],
|
||||
};
|
||||
checkboxSelect: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
watch: {
|
||||
selectedList: {
|
||||
handler(val) {
|
||||
this.selectMember = JSON.parse(JSON.stringify(val));
|
||||
this.init(this.data);
|
||||
});
|
||||
const emit = defineEmits(["callback"]);
|
||||
|
||||
// 模板 el-form/el-table 的模板 ref(原 ref="searchForm"/"addMemberForm"/"form" 等与 data 同名会互相覆盖,
|
||||
// 改名为 xxxRef;ossManage 与 import 同名,改名 ossManageRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
const addMemberFormRef = ref(null);
|
||||
const formRef = ref(null);
|
||||
const walletIncreaseFormRef = ref(null);
|
||||
const memberPointFormRef = ref(null);
|
||||
const memberGroupFormRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
const descTitle = ref("");
|
||||
const descFlag = ref(false);
|
||||
const loading = ref(true);
|
||||
const addFlag = ref(false);
|
||||
const addMemberForm = ref({
|
||||
mobile: "",
|
||||
username: "",
|
||||
password: "",
|
||||
});
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
order: "desc",
|
||||
id: "",
|
||||
username: "",
|
||||
mobile: "",
|
||||
disabled: "OPEN",
|
||||
groupId: "",
|
||||
});
|
||||
const picModelFlag = ref(false);
|
||||
const form = ref({});
|
||||
const walletIncreaseFlag = ref(false);
|
||||
const walletIncreaseLoading = ref(false);
|
||||
const walletIncreaseForm = ref({
|
||||
memberId: "",
|
||||
rechargeMoney: null,
|
||||
});
|
||||
const walletIncreaseRule = ref({
|
||||
rechargeMoney: [
|
||||
{ required: true, type: "number", message: "请输入充值金额", trigger: "change" },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (typeof value !== "number" || value <= 0) {
|
||||
callback(new Error("充值金额必须大于0"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
deep: true,
|
||||
immediate: true,
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
});
|
||||
const memberPointFlag = ref(false);
|
||||
const memberPointLoading = ref(false);
|
||||
const memberPointForm = ref({
|
||||
memberId: "",
|
||||
point: null,
|
||||
type: "INCREASE",
|
||||
});
|
||||
const selectedRows = ref([]);
|
||||
const memberGroupFlag = ref(false);
|
||||
const memberGroupLoading = ref(false);
|
||||
const memberGroupForm = ref({
|
||||
groupId: "",
|
||||
});
|
||||
const memberGroupRule = ref({
|
||||
groupId: [{ required: true, message: "请选择会员分组", trigger: "change" }],
|
||||
});
|
||||
const memberGroupList = ref([]);
|
||||
const memberGradeList = ref([]);
|
||||
const memberPointRule = ref({
|
||||
type: [{ required: true, message: "请选择类型", trigger: "change" }],
|
||||
point: [
|
||||
{ required: true, type: "number", message: "请输入积分", trigger: "change" },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (typeof value !== "number" || value <= 0) {
|
||||
callback(new Error("积分必须大于0"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
});
|
||||
const addRule = ref({
|
||||
mobile: [
|
||||
{ required: true, message: "请输入手机号码" },
|
||||
{ pattern: RegExp.mobile, message: "请输入正确的手机号" },
|
||||
],
|
||||
username: [{ required: true, message: "请输入客户名称" }],
|
||||
password: [{ required: true, message: "请输入密码" }],
|
||||
});
|
||||
const ruleValidate = ref({});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const selectMember = ref([]);
|
||||
|
||||
watch(
|
||||
() => props.selectedList,
|
||||
(val) => {
|
||||
selectMember.value = JSON.parse(JSON.stringify(val));
|
||||
init(data.value);
|
||||
},
|
||||
methods: {
|
||||
handleMoreCommand(name, row) {
|
||||
if (name === "edit") this.editPerm(row);
|
||||
if (name === "disabled") this.disabled(row);
|
||||
if (name === "increaseWallet") this.openWalletIncrease(row);
|
||||
if (name === "updatePoint") this.openMemberPoint(row);
|
||||
},
|
||||
onSelectionChange(selection) {
|
||||
if (this.selectedMember && this.checkboxSelect) {
|
||||
this.selectMember = selection || [];
|
||||
return;
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
function handleMoreCommand(name, row) {
|
||||
if (name === "edit") editPerm(row);
|
||||
if (name === "disabled") disabled(row);
|
||||
if (name === "increaseWallet") openWalletIncrease(row);
|
||||
if (name === "updatePoint") openMemberPoint(row);
|
||||
}
|
||||
function onSelectionChange(selection) {
|
||||
if (props.selectedMember && props.checkboxSelect) {
|
||||
selectMember.value = selection || [];
|
||||
return;
|
||||
}
|
||||
selectedRows.value = selection || [];
|
||||
}
|
||||
function getSelection() {
|
||||
return selectMember.value || [];
|
||||
}
|
||||
function syncTableSelection() {
|
||||
if (!props.selectedMember || !props.checkboxSelect) {
|
||||
return;
|
||||
}
|
||||
nextTick(() => {
|
||||
clearSelection();
|
||||
data.value.forEach((row) => {
|
||||
if (selectMember.value.some((member) => member.id === row.id)) {
|
||||
table.value?.toggleRowSelection(row, true);
|
||||
}
|
||||
this.selectedRows = selection || [];
|
||||
},
|
||||
getSelection() {
|
||||
return this.selectMember || [];
|
||||
},
|
||||
syncTableSelection() {
|
||||
if (!this.selectedMember || !this.checkboxSelect) {
|
||||
return;
|
||||
});
|
||||
});
|
||||
}
|
||||
function openSetMemberGroup() {
|
||||
memberGroupFlag.value = true;
|
||||
memberGroupLoading.value = false;
|
||||
memberGroupForm.value = { groupId: "" };
|
||||
nextTick(() => {
|
||||
if (memberGroupFormRef.value) memberGroupFormRef.value.resetFields();
|
||||
});
|
||||
loadMemberGroupList();
|
||||
}
|
||||
function loadMemberGroupList() {
|
||||
API_Member.getMemberGroupByPage({ pageNumber: 1, pageSize: 1000 }).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
memberGroupList.value = res.result.records || [];
|
||||
}
|
||||
});
|
||||
}
|
||||
function loadMemberGradeList() {
|
||||
API_Member.getMemberGradeByPage({ pageNumber: 1, pageSize: 1000 }).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
memberGradeList.value = res.result || [];
|
||||
}
|
||||
});
|
||||
}
|
||||
function clearSelection() {
|
||||
table.value?.clearSelection();
|
||||
}
|
||||
function submitSetMemberGroup() {
|
||||
if (selectedRows.value.length === 0) {
|
||||
ElMessage.warning("请先选择会员");
|
||||
return;
|
||||
}
|
||||
memberGroupFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
const memberIds = selectedRows.value.map((item) => item.id);
|
||||
const groupId = memberGroupForm.value.groupId;
|
||||
memberGroupLoading.value = true;
|
||||
Promise.all(
|
||||
memberIds.map((memberId) => API_Member.setMemberUserGroups(memberId, [groupId]))
|
||||
)
|
||||
.then((results) => {
|
||||
memberGroupLoading.value = false;
|
||||
const failed = results.find((res) => !res || !res.success);
|
||||
if (failed) {
|
||||
ElMessage.error(failed.message || "设置失败");
|
||||
return;
|
||||
}
|
||||
ElMessage.success("设置成功");
|
||||
memberGroupFlag.value = false;
|
||||
selectedRows.value = [];
|
||||
clearSelection();
|
||||
getData();
|
||||
})
|
||||
.catch(() => {
|
||||
memberGroupLoading.value = false;
|
||||
ElMessage.error("设置失败,请稍后重试");
|
||||
});
|
||||
});
|
||||
}
|
||||
function callback(val) {
|
||||
selectMember.value.forEach((item) => {
|
||||
item.___selected = false;
|
||||
});
|
||||
val.___selected = !val.___selected;
|
||||
let findUser = selectMember.value.find((item) => item.id == val.id);
|
||||
if (!findUser) {
|
||||
selectMember.value.push(val);
|
||||
} else {
|
||||
selectMember.value.map((item, index) => {
|
||||
if (item.id == findUser.id) {
|
||||
selectMember.value.splice(index, 1);
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.clearSelection();
|
||||
this.data.forEach((row) => {
|
||||
if (this.selectMember.some((member) => member.id === row.id)) {
|
||||
this.$refs.table?.toggleRowSelection(row, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
openSetMemberGroup() {
|
||||
this.memberGroupFlag = true;
|
||||
this.memberGroupLoading = false;
|
||||
this.memberGroupForm = { groupId: "" };
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.memberGroupForm) this.$refs.memberGroupForm.resetFields();
|
||||
});
|
||||
this.loadMemberGroupList();
|
||||
},
|
||||
loadMemberGroupList() {
|
||||
API_Member.getMemberGroupByPage({ pageNumber: 1, pageSize: 1000 }).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
this.memberGroupList = res.result.records || [];
|
||||
});
|
||||
}
|
||||
emit("callback", val);
|
||||
}
|
||||
function init(list) {
|
||||
list.forEach((item) => {
|
||||
if (selectMember.value.length != 0) {
|
||||
selectMember.value.forEach((member) => {
|
||||
if (member.id == item.id) {
|
||||
item.___selected = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
loadMemberGradeList() {
|
||||
API_Member.getMemberGradeByPage({ pageNumber: 1, pageSize: 1000 }).then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
this.memberGradeList = res.result || [];
|
||||
} else {
|
||||
item.___selected = false;
|
||||
}
|
||||
});
|
||||
data.value = list;
|
||||
}
|
||||
function changePage() {
|
||||
getData();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.value.pageSize = 20;
|
||||
getData();
|
||||
}
|
||||
function editPerm(val) {
|
||||
descTitle.value = `查看用户 ${val.username}`;
|
||||
descFlag.value = true;
|
||||
getMemberInfo(val.id);
|
||||
}
|
||||
function addMember() {
|
||||
addMemberForm.value = {
|
||||
mobile: "",
|
||||
username: "",
|
||||
password: "",
|
||||
};
|
||||
addFlag.value = true;
|
||||
nextTick(() => {
|
||||
addMemberFormRef.value?.clearValidate();
|
||||
});
|
||||
}
|
||||
function getMemberInfo(id) {
|
||||
API_Member.getMemberInfoData(id).then((res) => {
|
||||
if (res.result) {
|
||||
form.value = res.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
if (!props.selectedMember) {
|
||||
selectedRows.value = [];
|
||||
clearSelection();
|
||||
}
|
||||
API_Member.getMemberListData(searchForm.value).then((res) => {
|
||||
if (res.result.records) {
|
||||
loading.value = false;
|
||||
init(res.result.records);
|
||||
total.value = res.result.total;
|
||||
syncTableSelection();
|
||||
}
|
||||
});
|
||||
}
|
||||
function openPicSelector() {
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
form.value.face = val.url;
|
||||
}
|
||||
function addMemberSubmit() {
|
||||
addMemberForm.value.password = md5(addMemberForm.value.password);
|
||||
addMemberFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
API_Member.addMember(addMemberForm.value).then((res) => {
|
||||
if (res.result) {
|
||||
addMemberFormRef.value.resetFields();
|
||||
getData();
|
||||
ElMessage.success("添加成功!");
|
||||
addFlag.value = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
clearSelection() {
|
||||
this.$refs.table?.clearSelection();
|
||||
},
|
||||
submitSetMemberGroup() {
|
||||
if (this.selectedRows.length === 0) {
|
||||
ElMessage.warning("请先选择会员");
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
function selectedRegion(val) {
|
||||
if (val.type === "select") {
|
||||
const paths = val.data.map((item) => item.name).join(",");
|
||||
const ids = val.data.map((item) => item.id).join(",");
|
||||
form.value.region = paths;
|
||||
form.value.regionId = ids;
|
||||
} else {
|
||||
form.value.region = val.data.addr;
|
||||
form.value.regionId = val.data.addrId;
|
||||
}
|
||||
}
|
||||
function detail(row) {
|
||||
customRouterPush({ name: "member-detail", query: { id: row.id } });
|
||||
}
|
||||
function disabled(v) {
|
||||
let params = {
|
||||
memberIds: [v.id],
|
||||
disabled: false,
|
||||
};
|
||||
ElMessageBox.confirm("确认禁用此会员?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.updateMemberStatus(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("禁用成功");
|
||||
getData();
|
||||
}
|
||||
this.$refs.memberGroupForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
const memberIds = this.selectedRows.map((item) => item.id);
|
||||
const groupId = this.memberGroupForm.groupId;
|
||||
this.memberGroupLoading = true;
|
||||
Promise.all(
|
||||
memberIds.map((memberId) => API_Member.setMemberUserGroups(memberId, [groupId]))
|
||||
)
|
||||
.then((results) => {
|
||||
this.memberGroupLoading = false;
|
||||
const failed = results.find((res) => !res || !res.success);
|
||||
if (failed) {
|
||||
ElMessage.error(failed.message || "设置失败");
|
||||
return;
|
||||
}
|
||||
ElMessage.success("设置成功");
|
||||
this.memberGroupFlag = false;
|
||||
this.selectedRows = [];
|
||||
this.clearSelection();
|
||||
this.getData();
|
||||
})
|
||||
.catch(() => {
|
||||
this.memberGroupLoading = false;
|
||||
ElMessage.error("设置失败,请稍后重试");
|
||||
});
|
||||
});
|
||||
},
|
||||
callback(val) {
|
||||
this.selectMember.forEach((item) => {
|
||||
item.___selected = false;
|
||||
});
|
||||
val.___selected = !val.___selected;
|
||||
let findUser = this.selectMember.find((item) => item.id == val.id);
|
||||
if (!findUser) {
|
||||
this.selectMember.push(val);
|
||||
} else {
|
||||
this.selectMember.map((item, index) => {
|
||||
if (item.id == findUser.id) {
|
||||
this.selectMember.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.$emit("callback", val);
|
||||
},
|
||||
init(data) {
|
||||
data.forEach((item) => {
|
||||
if (this.selectMember.length != 0) {
|
||||
this.selectMember.forEach((member) => {
|
||||
if (member.id == item.id) {
|
||||
item.___selected = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
function openWalletIncrease(row) {
|
||||
walletIncreaseLoading.value = false;
|
||||
walletIncreaseForm.value = { memberId: row.id, rechargeMoney: null };
|
||||
walletIncreaseFlag.value = true;
|
||||
nextTick(() => {
|
||||
walletIncreaseFormRef.value && walletIncreaseFormRef.value.resetFields();
|
||||
});
|
||||
}
|
||||
function submitWalletIncrease() {
|
||||
walletIncreaseFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
walletIncreaseLoading.value = true;
|
||||
API_Member.increaseMemberWallet({
|
||||
memberId: walletIncreaseForm.value.memberId,
|
||||
rechargeMoney: walletIncreaseForm.value.rechargeMoney,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("充值成功");
|
||||
walletIncreaseFlag.value = false;
|
||||
getData();
|
||||
} else {
|
||||
item.___selected = false;
|
||||
ElMessage.error((res && res.message) || "充值失败");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
walletIncreaseLoading.value = false;
|
||||
});
|
||||
this.data = data;
|
||||
},
|
||||
changePage() {
|
||||
this.getData();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getData();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getData();
|
||||
},
|
||||
editPerm(val) {
|
||||
this.descTitle = `查看用户 ${val.username}`;
|
||||
this.descFlag = true;
|
||||
this.getMemberInfo(val.id);
|
||||
},
|
||||
addMember() {
|
||||
this.addMemberForm = {
|
||||
mobile: "",
|
||||
username: "",
|
||||
password: "",
|
||||
};
|
||||
this.addFlag = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.addMemberForm?.clearValidate();
|
||||
});
|
||||
},
|
||||
getMemberInfo(id) {
|
||||
API_Member.getMemberInfoData(id).then((res) => {
|
||||
if (res.result) {
|
||||
this.form = res.result;
|
||||
});
|
||||
}
|
||||
function openMemberPoint(row) {
|
||||
memberPointLoading.value = false;
|
||||
memberPointForm.value = { memberId: row.id, point: null, type: "INCREASE" };
|
||||
memberPointFlag.value = true;
|
||||
nextTick(() => {
|
||||
memberPointFormRef.value && memberPointFormRef.value.resetFields();
|
||||
});
|
||||
}
|
||||
function submitMemberPoint() {
|
||||
memberPointFormRef.value.validate((valid) => {
|
||||
if (!valid) return;
|
||||
memberPointLoading.value = true;
|
||||
API_Member.updateMemberPoint({
|
||||
memberId: memberPointForm.value.memberId,
|
||||
point: memberPointForm.value.point,
|
||||
type: memberPointForm.value.type,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
memberPointFlag.value = false;
|
||||
getData();
|
||||
} else {
|
||||
ElMessage.error((res && res.message) || "修改失败");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
memberPointLoading.value = false;
|
||||
});
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
if (!this.selectedMember) {
|
||||
this.selectedRows = [];
|
||||
this.clearSelection();
|
||||
}
|
||||
API_Member.getMemberListData(this.searchForm).then((res) => {
|
||||
if (res.result.records) {
|
||||
this.loading = false;
|
||||
this.init(res.result.records);
|
||||
this.total = res.result.total;
|
||||
this.syncTableSelection();
|
||||
}
|
||||
});
|
||||
},
|
||||
openPicSelector() {
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
this.form.face = val.url;
|
||||
},
|
||||
addMemberSubmit() {
|
||||
this.addMemberForm.password = this.md5(this.addMemberForm.password);
|
||||
this.$refs.addMemberForm.validate((valid) => {
|
||||
if (valid) {
|
||||
API_Member.addMember(this.addMemberForm).then((res) => {
|
||||
if (res.result) {
|
||||
this.$refs.addMemberForm.resetFields();
|
||||
this.getData();
|
||||
ElMessage.success("添加成功!");
|
||||
this.addFlag = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
selectedRegion(val) {
|
||||
if (val.type === "select") {
|
||||
const paths = val.data.map((item) => item.name).join(",");
|
||||
const ids = val.data.map((item) => item.id).join(",");
|
||||
this.form.region = paths;
|
||||
this.form.regionId = ids;
|
||||
} else {
|
||||
this.form.region = val.data.addr;
|
||||
this.form.regionId = val.data.addrId;
|
||||
}
|
||||
},
|
||||
detail(row) {
|
||||
customRouterPush({ name: "member-detail", query: { id: row.id } });
|
||||
},
|
||||
disabled(v) {
|
||||
let params = {
|
||||
memberIds: [v.id],
|
||||
disabled: false,
|
||||
};
|
||||
ElMessageBox.confirm("确认禁用此会员?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.updateMemberStatus(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("禁用成功");
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
openWalletIncrease(row) {
|
||||
this.walletIncreaseLoading = false;
|
||||
this.walletIncreaseForm = { memberId: row.id, rechargeMoney: null };
|
||||
this.walletIncreaseFlag = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.walletIncreaseForm && this.$refs.walletIncreaseForm.resetFields();
|
||||
});
|
||||
},
|
||||
submitWalletIncrease() {
|
||||
this.$refs.walletIncreaseForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.walletIncreaseLoading = true;
|
||||
API_Member.increaseMemberWallet({
|
||||
memberId: this.walletIncreaseForm.memberId,
|
||||
rechargeMoney: this.walletIncreaseForm.rechargeMoney,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("充值成功");
|
||||
this.walletIncreaseFlag = false;
|
||||
this.getData();
|
||||
} else {
|
||||
ElMessage.error((res && res.message) || "充值失败");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.walletIncreaseLoading = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
openMemberPoint(row) {
|
||||
this.memberPointLoading = false;
|
||||
this.memberPointForm = { memberId: row.id, point: null, type: "INCREASE" };
|
||||
this.memberPointFlag = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.memberPointForm && this.$refs.memberPointForm.resetFields();
|
||||
});
|
||||
},
|
||||
submitMemberPoint() {
|
||||
this.$refs.memberPointForm.validate((valid) => {
|
||||
if (!valid) return;
|
||||
this.memberPointLoading = true;
|
||||
API_Member.updateMemberPoint({
|
||||
memberId: this.memberPointForm.memberId,
|
||||
point: this.memberPointForm.point,
|
||||
type: this.memberPointForm.type,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
this.memberPointFlag = false;
|
||||
this.getData();
|
||||
} else {
|
||||
ElMessage.error((res && res.message) || "修改失败");
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.memberPointLoading = false;
|
||||
});
|
||||
});
|
||||
},
|
||||
handleSubmitModal() {
|
||||
const { nickName, sex, username, face, newPassword, id, regionId, region } = this.form;
|
||||
let time = new Date(this.form.birthday);
|
||||
let birthday =
|
||||
this.form.birthday === undefined
|
||||
? ""
|
||||
: time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
|
||||
let submit = {
|
||||
regionId,
|
||||
region,
|
||||
nickName,
|
||||
sex,
|
||||
birthday,
|
||||
face,
|
||||
id,
|
||||
};
|
||||
if (newPassword) {
|
||||
submit.password = this.md5(newPassword);
|
||||
}
|
||||
API_Member.updateMember(submit).then((res) => {
|
||||
if (res.result) {
|
||||
ElMessage.success("修改成功!");
|
||||
this.descFlag = false;
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getData();
|
||||
this.loadMemberGroupList();
|
||||
this.loadMemberGradeList();
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
function handleSubmitModal() {
|
||||
const { nickName, sex, username, face, newPassword, id, regionId, region } = form.value;
|
||||
let time = new Date(form.value.birthday);
|
||||
let birthday =
|
||||
form.value.birthday === undefined
|
||||
? ""
|
||||
: time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
|
||||
let submit = {
|
||||
regionId,
|
||||
region,
|
||||
nickName,
|
||||
sex,
|
||||
birthday,
|
||||
face,
|
||||
id,
|
||||
};
|
||||
if (newPassword) {
|
||||
submit.password = md5(newPassword);
|
||||
}
|
||||
API_Member.updateMember(submit).then((res) => {
|
||||
if (res.result) {
|
||||
ElMessage.success("修改成功!");
|
||||
descFlag.value = false;
|
||||
getData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getData();
|
||||
loadMemberGroupList();
|
||||
loadMemberGradeList();
|
||||
});
|
||||
|
||||
// 外部通过 ref 调用(promotions/coupon-activity/coupon-publish.vue 的 memberLayoutRef.getSelection())
|
||||
defineExpose({ getSelection });
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.face {
|
||||
|
||||
@@ -379,7 +379,7 @@
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form ref="addressForm" :model="addressForm" label-width="100px" :rules="addressFormValidate">
|
||||
<el-form ref="addressFormRef" :model="addressForm" label-width="100px" :rules="addressFormValidate">
|
||||
<el-form-item label="收货人姓名" prop="name">
|
||||
<el-input v-model="addressForm.name" maxlength="8" clearable style="width: 80%" />
|
||||
</el-form-item>
|
||||
@@ -388,7 +388,7 @@
|
||||
</el-form-item>
|
||||
<el-form-item label="收货人地址" prop="consigneeAddressPath">
|
||||
<span>{{ addressForm.consigneeAddressPath || "暂无地址" }}</span>
|
||||
<el-button style="margin-left: 10px" @click="$refs.map.open()">选择</el-button>
|
||||
<el-button style="margin-left: 10px" @click="map.open()">选择</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="详细地址" prop="detail">
|
||||
<el-input v-model="addressForm.detail" maxlength="35" clearable style="width: 80%" />
|
||||
@@ -412,7 +412,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, onMounted } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
import multipleMap from "@/components/map/multiple-map";
|
||||
@@ -420,374 +422,373 @@ import * as RegExp from "@/libs/RegExp.js";
|
||||
import * as API_Order from "@/api/order.js";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
name: "memberDetail",
|
||||
components: {
|
||||
ossManage,
|
||||
multipleMap,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: "",
|
||||
activeTab: "point",
|
||||
loading: true,
|
||||
addressLoading: false,
|
||||
memberInfo: {},
|
||||
memberWalletInfo: {},
|
||||
addressModalTitle: "",
|
||||
addressModalVisible: false,
|
||||
addressForm: {
|
||||
id: "",
|
||||
isDefault: 0,
|
||||
consigneeAddressPath: "",
|
||||
consigneeAddressIdPath: "",
|
||||
},
|
||||
selectDate: null,
|
||||
submitLoading: false,
|
||||
addressFormValidate: {
|
||||
name: [{ required: true, message: "收货人姓名不能为空" }],
|
||||
mobile: [
|
||||
{ required: true, message: "请输入收货人手机号码" },
|
||||
{
|
||||
pattern: RegExp.mobile,
|
||||
message: "请输入正确的手机号",
|
||||
},
|
||||
],
|
||||
consigneeAddressPath: [{ required: true, message: "收货人地址不能为空" }],
|
||||
detail: [{ required: true, message: "收货人详细地址不能为空" }],
|
||||
},
|
||||
pointData: [],
|
||||
pointTotal: 0,
|
||||
pointSearchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
orderData: [],
|
||||
orderTotal: 0,
|
||||
orderSearchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
payStatus: "",
|
||||
orderSn: "",
|
||||
orderType: "",
|
||||
},
|
||||
addressData: [],
|
||||
addressTotal: 0,
|
||||
addressSearchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
walletSearchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
},
|
||||
walletData: [],
|
||||
walletTotal: 0,
|
||||
receiptRecordSearchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
},
|
||||
receiptRecordData: [],
|
||||
receiptRecordTotal: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
orderTypeText(v) {
|
||||
const map = {
|
||||
NORMAL: "普通订单",
|
||||
VIRTUAL: "虚拟订单",
|
||||
GIFT: "赠品订单",
|
||||
PINTUAN: "拼团订单",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
},
|
||||
clientTypeText(v) {
|
||||
const map = {
|
||||
H5: "移动端",
|
||||
PC: "PC端",
|
||||
WECHAT_MP: "小程序端",
|
||||
APP: "移动应用端",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
},
|
||||
orderStatusText(v) {
|
||||
const map = {
|
||||
UNPAID: "未付款",
|
||||
PAID: "已付款",
|
||||
UNDELIVERED: "待发货",
|
||||
DELIVERED: "已发货",
|
||||
COMPLETED: "已完成",
|
||||
TAKE: "待核验",
|
||||
CANCELLED: "已关闭",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
},
|
||||
payStatusText(v) {
|
||||
const map = {
|
||||
UNPAID: "未付款",
|
||||
PAID: "已付款",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
},
|
||||
groupAfterSaleStatusText(v) {
|
||||
const map = {
|
||||
NEW: "未申请",
|
||||
NOT_APPLIED: "未申请",
|
||||
ALREADY_APPLIED: "已申请",
|
||||
EXPIRED: "已失效",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
},
|
||||
groupComplainStatusText(v) {
|
||||
const map = {
|
||||
NEW: "未申请",
|
||||
NO_APPLY: "未申请",
|
||||
APPLYING: "申请中",
|
||||
COMPLETE: "已完成",
|
||||
EXPIRED: "已失效",
|
||||
CANCEL: "取消投诉",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
},
|
||||
walletServiceTypeText(type) {
|
||||
if (type === "WALLET_WITHDRAWAL") return "余额提现";
|
||||
if (type === "WALLET_PAY") return "余额支付";
|
||||
if (type === "WALLET_REFUND") return "余额退款";
|
||||
if (type === "WALLET_RECHARGE") return "余额充值";
|
||||
return "佣金提成";
|
||||
},
|
||||
init() {
|
||||
this.getMemberInfo();
|
||||
this.getPointData();
|
||||
},
|
||||
memberInfoChange(v) {
|
||||
if (v == "point") {
|
||||
this.getPointData();
|
||||
}
|
||||
if (v == "address") {
|
||||
this.$nextTick(() => {
|
||||
this.getAddressData();
|
||||
});
|
||||
}
|
||||
if (v == "order") {
|
||||
this.getOrderData();
|
||||
}
|
||||
if (v == "wallet") {
|
||||
this.getMemberWalletData();
|
||||
this.getDepositLogData();
|
||||
}
|
||||
if (v == "receipt") {
|
||||
this.getReceiptRecordData();
|
||||
}
|
||||
},
|
||||
getMemberInfo() {
|
||||
API_Member.getMemberInfoData(this.id).then((res) => {
|
||||
this.memberInfo = res.result;
|
||||
});
|
||||
},
|
||||
memberStatusChange(v) {
|
||||
let params = {
|
||||
memberIds: [this.id],
|
||||
disabled: v,
|
||||
};
|
||||
API_Member.updateMemberStatus(params).then((res) => {});
|
||||
},
|
||||
getMemberWalletData() {
|
||||
this.loading = true;
|
||||
let params = {
|
||||
memberId: this.id,
|
||||
};
|
||||
API_Member.getMemberWallet(params).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.memberWalletInfo = res.result;
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
},
|
||||
getDepositLogData() {
|
||||
this.loading = true;
|
||||
this.walletSearchForm.memberId = this.id;
|
||||
API_Member.getUserWallet(this.walletSearchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.walletData = res.result.records;
|
||||
this.walletTotal = res.result.total;
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
},
|
||||
getReceiptRecordData() {
|
||||
this.loading = true;
|
||||
this.receiptRecordSearchForm.pageNumber = 1;
|
||||
this.receiptRecordSearchForm.pageSize = 20;
|
||||
this.receiptRecordSearchForm.memberId = this.id;
|
||||
API_Order.getReceiptPage(this.receiptRecordSearchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.receiptRecordData = res.result.records;
|
||||
this.receiptRecordTotal = res.result.total;
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
},
|
||||
getOrderData() {
|
||||
this.loading = true;
|
||||
this.orderSearchForm.pageNumber = 1;
|
||||
this.orderSearchForm.pageSize = 20;
|
||||
this.orderSearchForm.memberId = this.id;
|
||||
API_Order.getOrderList(this.orderSearchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.orderData = res.result.records;
|
||||
this.orderTotal = res.result.total;
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
},
|
||||
orderDetail(v) {
|
||||
this.$router.push({
|
||||
name: "order-detail",
|
||||
query: { sn: v },
|
||||
});
|
||||
},
|
||||
getPointData() {
|
||||
this.loading = true;
|
||||
this.pointSearchForm.memberId = this.id;
|
||||
API_Member.getHistoryPointData(this.pointSearchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.pointData = res.result.records;
|
||||
this.pointTotal = res.result.total;
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
},
|
||||
addressSubmit() {
|
||||
this.addressForm.memberId = this.id;
|
||||
this.$refs.addressForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.submitLoading = true;
|
||||
let submit = JSON.parse(JSON.stringify(this.addressForm));
|
||||
submit.isDefault ? (submit.isDefault = true) : (submit.isDefault = false);
|
||||
if (submit.id != "") {
|
||||
API_Member.editMemberAddress(submit).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
this.addressModalVisible = false;
|
||||
this.getAddressData();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
API_Member.addMemberAddress(submit).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
this.addressModalVisible = false;
|
||||
this.getAddressData();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
getAddress(val) {
|
||||
if (val.type === "select") {
|
||||
const paths = val.data.map((item) => item.name).join(",");
|
||||
const ids = val.data.map((item) => item.id).join(",");
|
||||
defineOptions({ name: "memberDetail" });
|
||||
|
||||
this.addressForm.consigneeAddressPath = paths;
|
||||
this.addressForm.consigneeAddressIdPath = ids;
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const coord = val.data[val.data.length - 1].center.split(",");
|
||||
this.addressForm.lat = coord[1];
|
||||
this.addressForm.lon = coord[0];
|
||||
} else {
|
||||
this.addressForm.consigneeAddressPath = val.data.addr;
|
||||
this.addressForm.consigneeAddressIdPath = val.data.addrId;
|
||||
this.addressForm.lat = val.data.position.lat;
|
||||
this.addressForm.lon = val.data.position.lng;
|
||||
}
|
||||
// 模板 el-form 的 ref(原 ref="addressForm",与 data 同名会互相覆盖,改名为 addressFormRef)
|
||||
const addressFormRef = ref(null);
|
||||
// 模板 multipleMap 的 ref(ref="map")
|
||||
const map = ref(null);
|
||||
|
||||
const id = ref("");
|
||||
const activeTab = ref("point");
|
||||
const loading = ref(true);
|
||||
const addressLoading = ref(false);
|
||||
const memberInfo = ref({});
|
||||
const memberWalletInfo = ref({});
|
||||
const addressModalTitle = ref("");
|
||||
const addressModalVisible = ref(false);
|
||||
const addressForm = reactive({
|
||||
id: "",
|
||||
isDefault: 0,
|
||||
consigneeAddressPath: "",
|
||||
consigneeAddressIdPath: "",
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const submitLoading = ref(false);
|
||||
const addressFormValidate = reactive({
|
||||
name: [{ required: true, message: "收货人姓名不能为空" }],
|
||||
mobile: [
|
||||
{ required: true, message: "请输入收货人手机号码" },
|
||||
{
|
||||
pattern: RegExp.mobile,
|
||||
message: "请输入正确的手机号",
|
||||
},
|
||||
memberAddressRemove(v) {
|
||||
ElMessageBox.confirm("确定要删除此收货地址?", "删除", { type: "warning" }).then(() => {
|
||||
API_Member.removeMemberAddress(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
this.getAddressData();
|
||||
],
|
||||
consigneeAddressPath: [{ required: true, message: "收货人地址不能为空" }],
|
||||
detail: [{ required: true, message: "收货人详细地址不能为空" }],
|
||||
});
|
||||
const pointData = ref([]);
|
||||
const pointTotal = ref(0);
|
||||
const pointSearchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const orderData = ref([]);
|
||||
const orderTotal = ref(0);
|
||||
const orderSearchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
payStatus: "",
|
||||
orderSn: "",
|
||||
orderType: "",
|
||||
});
|
||||
const addressData = ref([]);
|
||||
const addressTotal = ref(0);
|
||||
const addressSearchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const walletSearchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
});
|
||||
const walletData = ref([]);
|
||||
const walletTotal = ref(0);
|
||||
const receiptRecordSearchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
});
|
||||
const receiptRecordData = ref([]);
|
||||
const receiptRecordTotal = ref(0);
|
||||
|
||||
function orderTypeText(v) {
|
||||
const map = {
|
||||
NORMAL: "普通订单",
|
||||
VIRTUAL: "虚拟订单",
|
||||
GIFT: "赠品订单",
|
||||
PINTUAN: "拼团订单",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
}
|
||||
function clientTypeText(v) {
|
||||
const map = {
|
||||
H5: "移动端",
|
||||
PC: "PC端",
|
||||
WECHAT_MP: "小程序端",
|
||||
APP: "移动应用端",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
}
|
||||
function orderStatusText(v) {
|
||||
const map = {
|
||||
UNPAID: "未付款",
|
||||
PAID: "已付款",
|
||||
UNDELIVERED: "待发货",
|
||||
DELIVERED: "已发货",
|
||||
COMPLETED: "已完成",
|
||||
TAKE: "待核验",
|
||||
CANCELLED: "已关闭",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
}
|
||||
function payStatusText(v) {
|
||||
const map = {
|
||||
UNPAID: "未付款",
|
||||
PAID: "已付款",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
}
|
||||
function groupAfterSaleStatusText(v) {
|
||||
const map = {
|
||||
NEW: "未申请",
|
||||
NOT_APPLIED: "未申请",
|
||||
ALREADY_APPLIED: "已申请",
|
||||
EXPIRED: "已失效",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
}
|
||||
function groupComplainStatusText(v) {
|
||||
const map = {
|
||||
NEW: "未申请",
|
||||
NO_APPLY: "未申请",
|
||||
APPLYING: "申请中",
|
||||
COMPLETE: "已完成",
|
||||
EXPIRED: "已失效",
|
||||
CANCEL: "取消投诉",
|
||||
};
|
||||
return map[v] || v || "";
|
||||
}
|
||||
function walletServiceTypeText(type) {
|
||||
if (type === "WALLET_WITHDRAWAL") return "余额提现";
|
||||
if (type === "WALLET_PAY") return "余额支付";
|
||||
if (type === "WALLET_REFUND") return "余额退款";
|
||||
if (type === "WALLET_RECHARGE") return "余额充值";
|
||||
return "佣金提成";
|
||||
}
|
||||
function init() {
|
||||
getMemberInfo();
|
||||
getPointData();
|
||||
}
|
||||
function memberInfoChange(v) {
|
||||
if (v == "point") {
|
||||
getPointData();
|
||||
}
|
||||
if (v == "address") {
|
||||
nextTick(() => {
|
||||
getAddressData();
|
||||
});
|
||||
}
|
||||
if (v == "order") {
|
||||
getOrderData();
|
||||
}
|
||||
if (v == "wallet") {
|
||||
getMemberWalletData();
|
||||
getDepositLogData();
|
||||
}
|
||||
if (v == "receipt") {
|
||||
getReceiptRecordData();
|
||||
}
|
||||
}
|
||||
function getMemberInfo() {
|
||||
API_Member.getMemberInfoData(id.value).then((res) => {
|
||||
memberInfo.value = res.result;
|
||||
});
|
||||
}
|
||||
function memberStatusChange(v) {
|
||||
let params = {
|
||||
memberIds: [id.value],
|
||||
disabled: v,
|
||||
};
|
||||
API_Member.updateMemberStatus(params).then((res) => {});
|
||||
}
|
||||
function getMemberWalletData() {
|
||||
loading.value = true;
|
||||
let params = {
|
||||
memberId: id.value,
|
||||
};
|
||||
API_Member.getMemberWallet(params).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
memberWalletInfo.value = res.result;
|
||||
}
|
||||
});
|
||||
loading.value = false;
|
||||
}
|
||||
function getDepositLogData() {
|
||||
loading.value = true;
|
||||
walletSearchForm.value.memberId = id.value;
|
||||
API_Member.getUserWallet(walletSearchForm.value).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
walletData.value = res.result.records;
|
||||
walletTotal.value = res.result.total;
|
||||
}
|
||||
});
|
||||
loading.value = false;
|
||||
}
|
||||
function getReceiptRecordData() {
|
||||
loading.value = true;
|
||||
receiptRecordSearchForm.value.pageNumber = 1;
|
||||
receiptRecordSearchForm.value.pageSize = 20;
|
||||
receiptRecordSearchForm.value.memberId = id.value;
|
||||
API_Order.getReceiptPage(receiptRecordSearchForm.value).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
receiptRecordData.value = res.result.records;
|
||||
receiptRecordTotal.value = res.result.total;
|
||||
}
|
||||
});
|
||||
loading.value = false;
|
||||
}
|
||||
function getOrderData() {
|
||||
loading.value = true;
|
||||
orderSearchForm.value.pageNumber = 1;
|
||||
orderSearchForm.value.pageSize = 20;
|
||||
orderSearchForm.value.memberId = id.value;
|
||||
API_Order.getOrderList(orderSearchForm.value).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
orderData.value = res.result.records;
|
||||
orderTotal.value = res.result.total;
|
||||
}
|
||||
});
|
||||
loading.value = false;
|
||||
}
|
||||
function orderDetail(v) {
|
||||
router.push({
|
||||
name: "order-detail",
|
||||
query: { sn: v },
|
||||
});
|
||||
}
|
||||
function getPointData() {
|
||||
loading.value = true;
|
||||
pointSearchForm.value.memberId = id.value;
|
||||
API_Member.getHistoryPointData(pointSearchForm.value).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
pointData.value = res.result.records;
|
||||
pointTotal.value = res.result.total;
|
||||
}
|
||||
});
|
||||
loading.value = false;
|
||||
}
|
||||
function addressSubmit() {
|
||||
addressForm.memberId = id.value;
|
||||
addressFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true;
|
||||
let submit = JSON.parse(JSON.stringify(addressForm));
|
||||
submit.isDefault ? (submit.isDefault = true) : (submit.isDefault = false);
|
||||
if (submit.id != "") {
|
||||
API_Member.editMemberAddress(submit).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
addressModalVisible.value = false;
|
||||
getAddressData();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
getAddressData() {
|
||||
this.addressLoading = true;
|
||||
API_Member.getMemberAddressData(this.id, this.addressSearchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.addressData = res.result.records;
|
||||
this.addressTotal = res.result.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.addressLoading = false;
|
||||
});
|
||||
},
|
||||
pointChangePage(v) {
|
||||
this.pointSearchForm.pageNumber = v;
|
||||
this.getPointData();
|
||||
},
|
||||
pointChangePageSize(v) {
|
||||
this.pointSearchForm.pageNumber = 1;
|
||||
this.pointSearchForm.pageSize = v;
|
||||
this.getPointData();
|
||||
},
|
||||
addressChangePage(v) {
|
||||
this.addressSearchForm.pageNumber = v;
|
||||
this.getAddressData();
|
||||
},
|
||||
addressChangePageSize(v) {
|
||||
this.addressSearchForm.pageNumber = 1;
|
||||
this.addressSearchForm.pageSize = v;
|
||||
this.getAddressData();
|
||||
},
|
||||
walletChangePage(v) {
|
||||
this.walletSearchForm.pageNumber = v;
|
||||
this.getDepositLogData();
|
||||
},
|
||||
walletChangePageSize(v) {
|
||||
this.walletSearchForm.pageNumber = 1;
|
||||
this.walletSearchForm.pageSize = v;
|
||||
this.getDepositLogData();
|
||||
},
|
||||
orderChangePage(v) {
|
||||
this.orderSearchForm.pageNumber = v;
|
||||
this.getOrderData();
|
||||
},
|
||||
orderChangePageSize(v) {
|
||||
this.orderSearchForm.pageNumber = 1;
|
||||
this.orderSearchForm.pageSize = v;
|
||||
this.getOrderData();
|
||||
},
|
||||
selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
this.orderSearchForm.startDate = v[0];
|
||||
this.orderSearchForm.endDate = v[1];
|
||||
} else {
|
||||
delete this.orderSearchForm.startDate;
|
||||
delete this.orderSearchForm.endDate;
|
||||
API_Member.addMemberAddress(submit).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res && res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
addressModalVisible.value = false;
|
||||
getAddressData();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.id = this.$route.query.id;
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
function getAddress(val) {
|
||||
if (val.type === "select") {
|
||||
const paths = val.data.map((item) => item.name).join(",");
|
||||
const ids = val.data.map((item) => item.id).join(",");
|
||||
|
||||
addressForm.consigneeAddressPath = paths;
|
||||
addressForm.consigneeAddressIdPath = ids;
|
||||
|
||||
const coord = val.data[val.data.length - 1].center.split(",");
|
||||
addressForm.lat = coord[1];
|
||||
addressForm.lon = coord[0];
|
||||
} else {
|
||||
addressForm.consigneeAddressPath = val.data.addr;
|
||||
addressForm.consigneeAddressIdPath = val.data.addrId;
|
||||
addressForm.lat = val.data.position.lat;
|
||||
addressForm.lon = val.data.position.lng;
|
||||
}
|
||||
}
|
||||
function memberAddressRemove(v) {
|
||||
ElMessageBox.confirm("确定要删除此收货地址?", "删除", { type: "warning" }).then(() => {
|
||||
API_Member.removeMemberAddress(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("删除成功");
|
||||
getAddressData();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
function getAddressData() {
|
||||
addressLoading.value = true;
|
||||
API_Member.getMemberAddressData(id.value, addressSearchForm.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
addressData.value = res.result.records;
|
||||
addressTotal.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
addressLoading.value = false;
|
||||
});
|
||||
}
|
||||
function pointChangePage(v) {
|
||||
pointSearchForm.value.pageNumber = v;
|
||||
getPointData();
|
||||
}
|
||||
function pointChangePageSize(v) {
|
||||
pointSearchForm.value.pageNumber = 1;
|
||||
pointSearchForm.value.pageSize = v;
|
||||
getPointData();
|
||||
}
|
||||
function addressChangePage(v) {
|
||||
addressSearchForm.value.pageNumber = v;
|
||||
getAddressData();
|
||||
}
|
||||
function addressChangePageSize(v) {
|
||||
addressSearchForm.value.pageNumber = 1;
|
||||
addressSearchForm.value.pageSize = v;
|
||||
getAddressData();
|
||||
}
|
||||
function walletChangePage(v) {
|
||||
walletSearchForm.value.pageNumber = v;
|
||||
getDepositLogData();
|
||||
}
|
||||
function walletChangePageSize(v) {
|
||||
walletSearchForm.value.pageNumber = 1;
|
||||
walletSearchForm.value.pageSize = v;
|
||||
getDepositLogData();
|
||||
}
|
||||
function orderChangePage(v) {
|
||||
orderSearchForm.value.pageNumber = v;
|
||||
getOrderData();
|
||||
}
|
||||
function orderChangePageSize(v) {
|
||||
orderSearchForm.value.pageNumber = 1;
|
||||
orderSearchForm.value.pageSize = v;
|
||||
getOrderData();
|
||||
}
|
||||
function selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
orderSearchForm.value.startDate = v[0];
|
||||
orderSearchForm.value.endDate = v[1];
|
||||
} else {
|
||||
delete orderSearchForm.value.startDate;
|
||||
delete orderSearchForm.value.endDate;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
id.value = route.query.id;
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "memberDetail.scss";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -96,7 +96,7 @@
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="descFlag" :title="descTitle" width="500px" destroy-on-close>
|
||||
<el-form ref="formValidate" :model="formValidate" :rules="ruleValidate" label-width="80px">
|
||||
<el-form ref="formValidateRef" :model="formValidate" :rules="ruleValidate" label-width="80px">
|
||||
<el-form-item label="头像">
|
||||
<img :src="formValidate.face" class="face" />
|
||||
<el-button
|
||||
@@ -143,7 +143,7 @@
|
||||
|
||||
<el-dialog v-model="picModelFlag" width="1200px">
|
||||
<ossManage
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
@callback="callbackSelected"
|
||||
:isComponent="true"
|
||||
:initialize="picModelFlag"
|
||||
@@ -153,152 +153,156 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick, onMounted } from "vue";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
import multipleMap from "@/components/map/multiple-map";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
import { md5 } from "@/utils/md5.js";
|
||||
|
||||
export default {
|
||||
name: "memberRecycle",
|
||||
components: {
|
||||
ossManage,
|
||||
multipleMap,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedMember: false,
|
||||
descTitle: "",
|
||||
descFlag: false,
|
||||
openSearch: true,
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
order: "desc",
|
||||
id: "",
|
||||
username: "",
|
||||
mobile: "",
|
||||
disabled: "CLOSE",
|
||||
},
|
||||
picModelFlag: false,
|
||||
formValidate: {},
|
||||
ruleValidate: {},
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
callback(val) {
|
||||
this.$emit("callback", val);
|
||||
},
|
||||
init() {
|
||||
this.getData();
|
||||
},
|
||||
changePage() {
|
||||
this.getData();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getData();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getData();
|
||||
},
|
||||
editPerm(val) {
|
||||
this.descTitle = `查看用户 ${val.username}`;
|
||||
this.descFlag = true;
|
||||
this.getMemberInfo(val.id);
|
||||
},
|
||||
getMemberInfo(id) {
|
||||
API_Member.getMemberInfoData(id).then((res) => {
|
||||
if (res.result) {
|
||||
this.formValidate = res.result;
|
||||
}
|
||||
});
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
API_Member.getMemberListData(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
openPicSelector() {
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
this.formValidate.face = val.url;
|
||||
},
|
||||
selectedRegion(val) {
|
||||
if (val.type === "select") {
|
||||
const paths = val.data.map((item) => item.name).join(",");
|
||||
const ids = val.data.map((item) => item.id).join(",");
|
||||
this.formValidate.region = paths;
|
||||
this.formValidate.regionId = ids;
|
||||
} else {
|
||||
this.formValidate.region = val.data.addr;
|
||||
this.formValidate.regionId = val.data.addrId;
|
||||
defineOptions({ name: "memberRecycle" });
|
||||
|
||||
const emit = defineEmits(["callback"]);
|
||||
|
||||
// 模板 el-form 的 ref(原 ref="searchForm",与 data 同名会互相覆盖,改名为 searchFormRef)
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
// 模板编辑弹窗 el-form 的 ref(原 ref="formValidate",与 data 同名会互相覆盖,改名为 formValidateRef)
|
||||
const formValidateRef = ref(null);
|
||||
// 模板 ossManage 组件的 ref(原 ref="ossManage",与组件 import 同名会冲突,改名为 ossManageRef)
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
const selectedMember = ref(false);
|
||||
const descTitle = ref("");
|
||||
const descFlag = ref(false);
|
||||
const openSearch = ref(true);
|
||||
const loading = ref(true);
|
||||
const searchForm = ref({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
order: "desc",
|
||||
id: "",
|
||||
username: "",
|
||||
mobile: "",
|
||||
disabled: "CLOSE",
|
||||
});
|
||||
const picModelFlag = ref(false);
|
||||
const formValidate = ref({});
|
||||
const ruleValidate = ref({});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
function callback(val) {
|
||||
emit("callback", val);
|
||||
}
|
||||
function init() {
|
||||
getData();
|
||||
}
|
||||
function changePage() {
|
||||
getData();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getData();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.value.pageSize = 20;
|
||||
getData();
|
||||
}
|
||||
function editPerm(val) {
|
||||
descTitle.value = `查看用户 ${val.username}`;
|
||||
descFlag.value = true;
|
||||
getMemberInfo(val.id);
|
||||
}
|
||||
function getMemberInfo(id) {
|
||||
API_Member.getMemberInfoData(id).then((res) => {
|
||||
if (res.result) {
|
||||
formValidate.value = res.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
API_Member.getMemberListData(searchForm.value).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function openPicSelector() {
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
formValidate.value.face = val.url;
|
||||
}
|
||||
function selectedRegion(val) {
|
||||
if (val.type === "select") {
|
||||
const paths = val.data.map((item) => item.name).join(",");
|
||||
const ids = val.data.map((item) => item.id).join(",");
|
||||
formValidate.value.region = paths;
|
||||
formValidate.value.regionId = ids;
|
||||
} else {
|
||||
formValidate.value.region = val.data.addr;
|
||||
formValidate.value.regionId = val.data.addrId;
|
||||
}
|
||||
}
|
||||
function detail(row) {
|
||||
customRouterPush({ name: "member-detail", query: { id: row.id } });
|
||||
}
|
||||
function enable(v) {
|
||||
let params = {
|
||||
memberIds: [v.id],
|
||||
disabled: true,
|
||||
};
|
||||
ElMessageBox.confirm("确定启用此会员?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.updateMemberStatus(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("启用成功");
|
||||
getData();
|
||||
}
|
||||
},
|
||||
detail(row) {
|
||||
customRouterPush({ name: "member-detail", query: { id: row.id } });
|
||||
},
|
||||
enable(v) {
|
||||
let params = {
|
||||
memberIds: [v.id],
|
||||
disabled: true,
|
||||
};
|
||||
ElMessageBox.confirm("确定启用此会员?", "提示", { type: "warning" }).then(() => {
|
||||
API_Member.updateMemberStatus(params).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("启用成功");
|
||||
this.getData();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
handleSubmitModal() {
|
||||
const { nickName, sex, username, face, newPassword, id, regionId, region } = this.formValidate;
|
||||
let time = new Date(this.formValidate.birthday);
|
||||
let birthday = time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
|
||||
let submit = {
|
||||
regionId: regionId,
|
||||
region: region,
|
||||
nickName,
|
||||
sex,
|
||||
birthday,
|
||||
face: face || "",
|
||||
id,
|
||||
};
|
||||
if (newPassword) {
|
||||
submit.password = this.md5(newPassword);
|
||||
}
|
||||
API_Member.updateMember(submit).then((res) => {
|
||||
if (res.result) {
|
||||
ElMessage.success("修改成功!");
|
||||
this.descFlag = false;
|
||||
this.init();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
function handleSubmitModal() {
|
||||
const { nickName, sex, username, face, newPassword, id, regionId, region } = formValidate.value;
|
||||
let time = new Date(formValidate.value.birthday);
|
||||
let birthday = time.getFullYear() + "-" + (time.getMonth() + 1) + "-" + time.getDate();
|
||||
let submit = {
|
||||
regionId: regionId,
|
||||
region: region,
|
||||
nickName,
|
||||
sex,
|
||||
birthday,
|
||||
face: face || "",
|
||||
id,
|
||||
};
|
||||
if (newPassword) {
|
||||
// 原 this.md5 为 main.js 注册的全局属性,改为具名导入(同 login.vue / userManage.vue)
|
||||
submit.password = md5(newPassword);
|
||||
}
|
||||
API_Member.updateMember(submit).then((res) => {
|
||||
if (res.result) {
|
||||
ElMessage.success("修改成功!");
|
||||
descFlag.value = false;
|
||||
init();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.face {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog v-model="wechatModal" title="微信设置" width="530px">
|
||||
<el-form ref="wechatFormData" :model="wechatFormData" label-width="100px">
|
||||
<el-form ref="wechatFormRef" :model="wechatFormData" label-width="100px">
|
||||
<el-form-item v-if="tab === 'WECHAT'" label="模板名称">
|
||||
<el-input v-model="wechatFormData.name" maxlength="9" disabled />
|
||||
</el-form-item>
|
||||
@@ -105,171 +105,169 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import {
|
||||
wechatMessageSync,
|
||||
getWechatMessagePage,
|
||||
getWechatMessagePage as fetchWechatMessagePage,
|
||||
editWechatMessageTemplate,
|
||||
delWechatMessageTemplate,
|
||||
wechatMPMessageSync,
|
||||
getWechatMPMessagePage,
|
||||
getWechatMPMessagePage as fetchWechatMPMessagePage,
|
||||
editWechatMPMessageTemplate,
|
||||
delWechatMPMessageTemplate,
|
||||
} from "@/api/setting";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
title: "wechat-message-manage",
|
||||
data() {
|
||||
return {
|
||||
wechatModal: false,
|
||||
wechatFormData: {},
|
||||
wechatMPFormData: {},
|
||||
tab: "WECHAT",
|
||||
searchForm: { type: "WECHAT" },
|
||||
loading: true,
|
||||
id: "",
|
||||
weChatSearchForm: { pageNumber: 1, pageSize: 20 },
|
||||
weChatMPSearchForm: { pageNumber: 1, pageSize: 20 },
|
||||
weChatData: [],
|
||||
weChatMPData: [],
|
||||
weChatTotal: 0,
|
||||
weChatMPTotal: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.type = this.tab;
|
||||
if (this.tab === "WECHAT") {
|
||||
this.weChatSearchForm.pageNumber = v;
|
||||
this.getWechatMessagePage();
|
||||
} else {
|
||||
this.weChatMPSearchForm.pageNumber = v;
|
||||
this.getWechatMPMessagePage();
|
||||
}
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.type = this.tab;
|
||||
if (this.tab === "WECHAT") {
|
||||
this.weChatSearchForm.pageSize = v;
|
||||
this.getWechatMessagePage();
|
||||
} else {
|
||||
this.weChatMPSearchForm.pageSize = v;
|
||||
this.getWechatMPMessagePage();
|
||||
}
|
||||
},
|
||||
wechatSettingAlert(v) {
|
||||
this.wechatFormData = v;
|
||||
this.id = v.id;
|
||||
this.wechatModal = true;
|
||||
},
|
||||
weChatSync(mp) {
|
||||
ElMessageBox.confirm("确认要初始化微信小程序消息订阅?", "提示", { type: "warning" }).then(() => {
|
||||
if (mp === "mp") {
|
||||
wechatMPMessageSync().then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("微信小程序消息订阅初始化");
|
||||
this.getWechatMPMessagePage();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
wechatMessageSync().then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("微信消息模板初始化成功");
|
||||
this.getWechatMessagePage();
|
||||
}
|
||||
});
|
||||
}
|
||||
}).catch(() => {});
|
||||
},
|
||||
wechatFormDataEdit() {
|
||||
this.$refs.wechatFormData.validate((valid) => {
|
||||
if (valid) {
|
||||
if (!this.wechatFormData.updateTime) {
|
||||
this.wechatFormData.updateTime = "";
|
||||
}
|
||||
editWechatMessageTemplate(this.id, this.wechatFormData).then((res) => {
|
||||
if (res.message === "success") {
|
||||
ElMessage.success("微信模板修改成功");
|
||||
this.wechatModal = false;
|
||||
this.getWechatMessagePage();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
wechatMPFormDataEdit() {
|
||||
this.$refs.wechatFormData.validate((valid) => {
|
||||
if (valid) {
|
||||
editWechatMPMessageTemplate(this.id, this.wechatMPFormData).then((res) => {
|
||||
if (res.message === "success") {
|
||||
ElMessage.success("微信消息订阅模板修改成功");
|
||||
this.wechatModal = false;
|
||||
this.getWechatMPMessagePage();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
delWeChat(v) {
|
||||
ElMessageBox.confirm("确定删除此模板?", "提示", { type: "warning" }).then(() => {
|
||||
if (this.tab === "WECHAT") {
|
||||
delWechatMessageTemplate(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("微信模板删除成功");
|
||||
this.getWechatMessagePage();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
delWechatMPMessageTemplate(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("微信消息订阅删除成功");
|
||||
this.getWechatMPMessagePage();
|
||||
}
|
||||
});
|
||||
}
|
||||
}).catch(() => {});
|
||||
},
|
||||
getDataList() {
|
||||
this.getWechatMessagePage();
|
||||
},
|
||||
getWechatMessagePage() {
|
||||
this.loading = true;
|
||||
getWechatMessagePage(this.weChatSearchForm).then((res) => {
|
||||
this.loading = false;
|
||||
defineOptions({ title: "wechat-message-manage" });
|
||||
|
||||
const wechatFormRef = ref(null);
|
||||
|
||||
const wechatModal = ref(false);
|
||||
const wechatFormData = ref({});
|
||||
const wechatMPFormData = ref({});
|
||||
const tab = ref("WECHAT");
|
||||
const searchForm = reactive({ type: "WECHAT" });
|
||||
const loading = ref(true);
|
||||
const id = ref("");
|
||||
const weChatSearchForm = reactive({ pageNumber: 1, pageSize: 20 });
|
||||
const weChatMPSearchForm = reactive({ pageNumber: 1, pageSize: 20 });
|
||||
const weChatData = ref([]);
|
||||
const weChatMPData = ref([]);
|
||||
const weChatTotal = ref(0);
|
||||
const weChatMPTotal = ref(0);
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.type = tab.value;
|
||||
if (tab.value === "WECHAT") {
|
||||
weChatSearchForm.pageNumber = v;
|
||||
getWechatMessagePage();
|
||||
} else {
|
||||
weChatMPSearchForm.pageNumber = v;
|
||||
getWechatMPMessagePage();
|
||||
}
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.type = tab.value;
|
||||
if (tab.value === "WECHAT") {
|
||||
weChatSearchForm.pageSize = v;
|
||||
getWechatMessagePage();
|
||||
} else {
|
||||
weChatMPSearchForm.pageSize = v;
|
||||
getWechatMPMessagePage();
|
||||
}
|
||||
}
|
||||
function wechatSettingAlert(v) {
|
||||
wechatFormData.value = v;
|
||||
id.value = v.id;
|
||||
wechatModal.value = true;
|
||||
}
|
||||
function weChatSync(mp) {
|
||||
ElMessageBox.confirm("确认要初始化微信小程序消息订阅?", "提示", { type: "warning" }).then(() => {
|
||||
if (mp === "mp") {
|
||||
wechatMPMessageSync().then((res) => {
|
||||
if (res.success) {
|
||||
this.weChatData = res.result.records;
|
||||
this.weChatTotal = res.result.total;
|
||||
ElMessage.success("微信小程序消息订阅初始化");
|
||||
getWechatMPMessagePage();
|
||||
}
|
||||
});
|
||||
},
|
||||
getWechatMPMessagePage() {
|
||||
this.loading = true;
|
||||
getWechatMPMessagePage(this.weChatMPSearchForm).then((res) => {
|
||||
this.loading = false;
|
||||
} else {
|
||||
wechatMessageSync().then((res) => {
|
||||
if (res.success) {
|
||||
this.weChatMPData = res.result.records;
|
||||
this.weChatMPTotal = res.result.total;
|
||||
ElMessage.success("微信消息模板初始化成功");
|
||||
getWechatMessagePage();
|
||||
}
|
||||
});
|
||||
},
|
||||
tabPaneChange(tab) {
|
||||
const v = tab.paneName || this.tab;
|
||||
this.searchForm.type = v;
|
||||
if (v === "WECHAT") {
|
||||
this.getWechatMessagePage();
|
||||
} else if (v === "WECHATMP") {
|
||||
this.getWechatMPMessagePage();
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
function wechatFormDataEdit() {
|
||||
wechatFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
if (!wechatFormData.value.updateTime) {
|
||||
wechatFormData.value.updateTime = "";
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
editWechatMessageTemplate(id.value, wechatFormData.value).then((res) => {
|
||||
if (res.message === "success") {
|
||||
ElMessage.success("微信模板修改成功");
|
||||
wechatModal.value = false;
|
||||
getWechatMessagePage();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function wechatMPFormDataEdit() {
|
||||
wechatFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
editWechatMPMessageTemplate(id.value, wechatMPFormData.value).then((res) => {
|
||||
if (res.message === "success") {
|
||||
ElMessage.success("微信消息订阅模板修改成功");
|
||||
wechatModal.value = false;
|
||||
getWechatMPMessagePage();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function delWeChat(v) {
|
||||
ElMessageBox.confirm("确定删除此模板?", "提示", { type: "warning" }).then(() => {
|
||||
if (tab.value === "WECHAT") {
|
||||
delWechatMessageTemplate(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("微信模板删除成功");
|
||||
getWechatMessagePage();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
delWechatMPMessageTemplate(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("微信消息订阅删除成功");
|
||||
getWechatMPMessagePage();
|
||||
}
|
||||
});
|
||||
}
|
||||
}).catch(() => {});
|
||||
}
|
||||
function getDataList() {
|
||||
getWechatMessagePage();
|
||||
}
|
||||
function getWechatMessagePage() {
|
||||
loading.value = true;
|
||||
fetchWechatMessagePage(weChatSearchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
weChatData.value = res.result.records;
|
||||
weChatTotal.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function getWechatMPMessagePage() {
|
||||
loading.value = true;
|
||||
fetchWechatMPMessagePage(weChatMPSearchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
weChatMPData.value = res.result.records;
|
||||
weChatMPTotal.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function tabPaneChange(tabPane) {
|
||||
const v = tabPane.paneName || tab.value;
|
||||
searchForm.type = v;
|
||||
if (v === "WECHAT") {
|
||||
getWechatMessagePage();
|
||||
} else if (v === "WECHATMP") {
|
||||
getWechatMPMessagePage();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.link-text {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<el-tab-pane label="积分列表" name="pointList">
|
||||
<el-card class="point-content-card">
|
||||
<el-form
|
||||
ref="memberSearchForm"
|
||||
ref="memberSearchFormRef"
|
||||
:model="memberSearchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -131,7 +131,7 @@
|
||||
<el-tab-pane label="积分增减记录" name="pointChangeRecord">
|
||||
<el-card class="point-content-card">
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -203,132 +203,135 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Member from "@/api/member.js";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "point",
|
||||
data() {
|
||||
return {
|
||||
activeTab: "pointList",
|
||||
loading: true,
|
||||
memberLoading: false,
|
||||
pointsStatistics: {
|
||||
totalPoint: 0,
|
||||
unUsedPoint: 0,
|
||||
},
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
memberSearchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
nickName: "",
|
||||
username: "",
|
||||
disabled: "",
|
||||
minPoint: null,
|
||||
maxPoint: null,
|
||||
},
|
||||
data: [],
|
||||
memberData: [],
|
||||
memberTotal: 0,
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
callback(val) {
|
||||
this.$emit("callback", val);
|
||||
},
|
||||
detail(row) {
|
||||
customRouterPush({ name: "member-detail", query: { id: row.id } });
|
||||
},
|
||||
init() {
|
||||
this.getStatistics();
|
||||
this.getMemberList();
|
||||
this.getData();
|
||||
},
|
||||
getStatistics() {
|
||||
API_Member.queryMemberPointsStatistics().then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
this.pointsStatistics = {
|
||||
totalPoint: res.result.totalPoint || 0,
|
||||
unUsedPoint: res.result.unUsedPoint || 0,
|
||||
};
|
||||
}
|
||||
});
|
||||
},
|
||||
formatNumber(value) {
|
||||
const numericValue = Number(value || 0);
|
||||
if (!Number.isFinite(numericValue)) return "0";
|
||||
return numericValue.toLocaleString();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getData();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getData();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getData();
|
||||
},
|
||||
handleMemberSearch() {
|
||||
this.memberSearchForm.pageNumber = 1;
|
||||
this.getMemberList();
|
||||
},
|
||||
changeMemberPage(v) {
|
||||
this.memberSearchForm.pageNumber = v;
|
||||
this.getMemberList();
|
||||
},
|
||||
changeMemberPageSize(v) {
|
||||
this.memberSearchForm.pageNumber = 1;
|
||||
this.memberSearchForm.pageSize = v;
|
||||
this.getMemberList();
|
||||
},
|
||||
getMemberList() {
|
||||
this.memberLoading = true;
|
||||
const params = {
|
||||
pageNumber: this.memberSearchForm.pageNumber,
|
||||
pageSize: this.memberSearchForm.pageSize,
|
||||
nickName: this.memberSearchForm.nickName || undefined,
|
||||
username: this.memberSearchForm.username || undefined,
|
||||
disabled: this.memberSearchForm.disabled || undefined,
|
||||
minPoint: this.memberSearchForm.minPoint,
|
||||
maxPoint: this.memberSearchForm.maxPoint,
|
||||
defineOptions({ name: "point" });
|
||||
|
||||
const emit = defineEmits(["callback"]);
|
||||
|
||||
// 模板 ref,script 内未使用
|
||||
const memberSearchFormRef = ref(null);
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const activeTab = ref("pointList");
|
||||
const loading = ref(true);
|
||||
const memberLoading = ref(false);
|
||||
const pointsStatistics = ref({
|
||||
totalPoint: 0,
|
||||
unUsedPoint: 0,
|
||||
});
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
});
|
||||
const memberSearchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
nickName: "",
|
||||
username: "",
|
||||
disabled: "",
|
||||
minPoint: null,
|
||||
maxPoint: null,
|
||||
});
|
||||
const data = ref([]);
|
||||
const memberData = ref([]);
|
||||
const memberTotal = ref(0);
|
||||
const total = ref(0);
|
||||
|
||||
function callback(val) {
|
||||
emit("callback", val);
|
||||
}
|
||||
function detail(row) {
|
||||
customRouterPush({ name: "member-detail", query: { id: row.id } });
|
||||
}
|
||||
function init() {
|
||||
getStatistics();
|
||||
getMemberList();
|
||||
getData();
|
||||
}
|
||||
function getStatistics() {
|
||||
API_Member.queryMemberPointsStatistics().then((res) => {
|
||||
if (res && res.success && res.result) {
|
||||
pointsStatistics.value = {
|
||||
totalPoint: res.result.totalPoint || 0,
|
||||
unUsedPoint: res.result.unUsedPoint || 0,
|
||||
};
|
||||
API_Member.getMemberListData(params)
|
||||
.then((res) => {
|
||||
this.memberLoading = false;
|
||||
if (res && res.success && res.result && res.result.records) {
|
||||
this.memberData = res.result.records;
|
||||
this.memberTotal = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.memberLoading = false;
|
||||
});
|
||||
},
|
||||
getData() {
|
||||
this.loading = true;
|
||||
API_Member.getHistoryPointData(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
function formatNumber(value) {
|
||||
const numericValue = Number(value || 0);
|
||||
if (!Number.isFinite(numericValue)) return "0";
|
||||
return numericValue.toLocaleString();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getData();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getData();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getData();
|
||||
}
|
||||
function handleMemberSearch() {
|
||||
memberSearchForm.pageNumber = 1;
|
||||
getMemberList();
|
||||
}
|
||||
function changeMemberPage(v) {
|
||||
memberSearchForm.pageNumber = v;
|
||||
getMemberList();
|
||||
}
|
||||
function changeMemberPageSize(v) {
|
||||
memberSearchForm.pageNumber = 1;
|
||||
memberSearchForm.pageSize = v;
|
||||
getMemberList();
|
||||
}
|
||||
function getMemberList() {
|
||||
memberLoading.value = true;
|
||||
const params = {
|
||||
pageNumber: memberSearchForm.pageNumber,
|
||||
pageSize: memberSearchForm.pageSize,
|
||||
nickName: memberSearchForm.nickName || undefined,
|
||||
username: memberSearchForm.username || undefined,
|
||||
disabled: memberSearchForm.disabled || undefined,
|
||||
minPoint: memberSearchForm.minPoint,
|
||||
maxPoint: memberSearchForm.maxPoint,
|
||||
};
|
||||
API_Member.getMemberListData(params)
|
||||
.then((res) => {
|
||||
memberLoading.value = false;
|
||||
if (res && res.success && res.result && res.result.records) {
|
||||
memberData.value = res.result.records;
|
||||
memberTotal.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
memberLoading.value = false;
|
||||
});
|
||||
}
|
||||
function getData() {
|
||||
loading.value = true;
|
||||
API_Member.getHistoryPointData(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.points-statistics-card {
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="modalVisible" :title="modalTitle" width="500px" :close-on-click-modal="false">
|
||||
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form ref="formRef" :model="form" label-width="100px" :rules="formValidate">
|
||||
<el-form-item label="售后原因" prop="reason">
|
||||
<el-input v-model="form.reason" maxlength="20" clearable style="width: 100%" />
|
||||
</el-form-item>
|
||||
@@ -67,116 +67,113 @@
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Order from "@/api/order";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
activeTab: "RETURN_MONEY",
|
||||
modalVisible: false,
|
||||
modalTitle: "",
|
||||
loading: true,
|
||||
submitLoading: false,
|
||||
form: { reason: "" },
|
||||
formValidate: {
|
||||
reason: [{ required: true, message: "请输入售后原因", trigger: "blur" }],
|
||||
},
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
serviceType: "RETURN_MONEY",
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onTabClick(tab) {
|
||||
this.handleClickType(tab.paneName);
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleClickType(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.searchForm.serviceType = v;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
API_Order.getAfterSaleReasonPage(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
add() {
|
||||
this.form.reason = "";
|
||||
this.modalVisible = true;
|
||||
this.modalTitle = "添加售后原因";
|
||||
},
|
||||
edit(v) {
|
||||
this.form.reason = v.reason;
|
||||
this.form.id = v.id;
|
||||
this.modalVisible = true;
|
||||
this.modalTitle = "修改售后原因";
|
||||
},
|
||||
handleSubmit() {
|
||||
this.form.serviceType = this.searchForm.serviceType;
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.submitLoading = true;
|
||||
if (this.modalTitle == "添加售后原因") {
|
||||
delete this.form.id;
|
||||
API_Order.addAfterSaleReason(this.form).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("添加成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
API_Order.editAfterSaleReason(this.form.id, this.form).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
this.getDataList();
|
||||
this.modalVisible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
remove(v) {
|
||||
ElMessageBox.confirm("确认要删除此售后原因?", "确认删除", { type: "warning" }).then(() => {
|
||||
API_Order.delAfterSaleReason(v.id).then((res) => {
|
||||
const activeTab = ref("RETURN_MONEY");
|
||||
const modalVisible = ref(false);
|
||||
const modalTitle = ref("");
|
||||
const loading = ref(true);
|
||||
const submitLoading = ref(false);
|
||||
const form = reactive({ reason: "" });
|
||||
const formValidate = reactive({
|
||||
reason: [{ required: true, message: "请输入售后原因", trigger: "blur" }],
|
||||
});
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
serviceType: "RETURN_MONEY",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
const formRef = ref(null);
|
||||
|
||||
function onTabClick(tab) {
|
||||
handleClickType(tab.paneName);
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleClickType(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
searchForm.serviceType = v;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.getAfterSaleReasonPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function add() {
|
||||
form.reason = "";
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = "添加售后原因";
|
||||
}
|
||||
function edit(v) {
|
||||
form.reason = v.reason;
|
||||
form.id = v.id;
|
||||
modalVisible.value = true;
|
||||
modalTitle.value = "修改售后原因";
|
||||
}
|
||||
function handleSubmit() {
|
||||
form.serviceType = searchForm.serviceType;
|
||||
formRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
submitLoading.value = true;
|
||||
if (modalTitle.value == "添加售后原因") {
|
||||
delete form.id;
|
||||
API_Order.addAfterSaleReason(form).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("售后原因已删除");
|
||||
this.getDataList();
|
||||
ElMessage.success("添加成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getDataList();
|
||||
},
|
||||
};
|
||||
} else {
|
||||
API_Order.editAfterSaleReason(form.id, form).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("修改成功");
|
||||
getDataList();
|
||||
modalVisible.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function remove(v) {
|
||||
ElMessageBox.confirm("确认要删除此售后原因?", "确认删除", { type: "warning" }).then(() => {
|
||||
API_Order.delAfterSaleReason(v.id).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("售后原因已删除");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getDataList();
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.link-text {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -167,194 +167,193 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import * as API_Order from "@/api/order";
|
||||
import { unitPrice, customRouterPush } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "after-sale-order",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderSn: "",
|
||||
memberName: "",
|
||||
serviceStatus: "",
|
||||
storeName: "",
|
||||
sn: "",
|
||||
keywords: "",
|
||||
},
|
||||
selectDate: null,
|
||||
data: [],
|
||||
total: 0,
|
||||
currentStatus: "",
|
||||
afterSaleNumData: {},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
serviceStatusWithCount() {
|
||||
return [
|
||||
{ title: "全部", value: "" },
|
||||
{
|
||||
title: `申请售后${this.afterSaleNumData.applyNum ? "(" + this.afterSaleNumData.applyNum + ")" : ""}`,
|
||||
value: "APPLY",
|
||||
},
|
||||
{
|
||||
title: `通过售后${this.afterSaleNumData.passNum ? "(" + this.afterSaleNumData.passNum + ")" : ""}`,
|
||||
value: "PASS",
|
||||
},
|
||||
{
|
||||
title: `拒绝售后${this.afterSaleNumData.refuseNum ? "(" + this.afterSaleNumData.refuseNum + ")" : ""}`,
|
||||
value: "REFUSE",
|
||||
},
|
||||
{
|
||||
title: `待收货${this.afterSaleNumData.buyerReturnNum ? "(" + this.afterSaleNumData.buyerReturnNum + ")" : ""}`,
|
||||
value: "BUYER_RETURN",
|
||||
},
|
||||
{
|
||||
title: `确认收货${this.afterSaleNumData.sellerConfirmNum ? "(" + this.afterSaleNumData.sellerConfirmNum + ")" : ""}`,
|
||||
value: "SELLER_CONFIRM",
|
||||
},
|
||||
{
|
||||
title: `完成售后${this.afterSaleNumData.completeNum ? "(" + this.afterSaleNumData.completeNum + ")" : ""}`,
|
||||
value: "COMPLETE",
|
||||
},
|
||||
{
|
||||
title: `卖家终止售后${this.afterSaleNumData.sellerTerminationNum ? "(" + this.afterSaleNumData.sellerTerminationNum + ")" : ""}`,
|
||||
value: "SELLER_TERMINATION",
|
||||
},
|
||||
{
|
||||
title: `买家取消售后${this.afterSaleNumData.buyerCancelNum ? "(" + this.afterSaleNumData.buyerCancelNum + ")" : ""}`,
|
||||
value: "BUYER_CANCEL",
|
||||
},
|
||||
{
|
||||
title: `等待平台退款${this.afterSaleNumData.waitRefundNum ? "(" + this.afterSaleNumData.waitRefundNum + ")" : ""}`,
|
||||
value: "WAIT_REFUND",
|
||||
},
|
||||
];
|
||||
defineOptions({ name: "after-sale-order" });
|
||||
|
||||
// 模板 ref(模板 ref="searchForm" 与 data 同名,改名 searchFormRef 避免互相覆盖)
|
||||
const searchFormRef = ref(null);
|
||||
// 模板 ref="table"
|
||||
const table = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderSn: "",
|
||||
memberName: "",
|
||||
serviceStatus: "",
|
||||
storeName: "",
|
||||
sn: "",
|
||||
keywords: "",
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const currentStatus = ref("");
|
||||
const afterSaleNumData = ref({});
|
||||
|
||||
const serviceStatusWithCount = computed(() => {
|
||||
return [
|
||||
{ title: "全部", value: "" },
|
||||
{
|
||||
title: `申请售后${afterSaleNumData.value.applyNum ? "(" + afterSaleNumData.value.applyNum + ")" : ""}`,
|
||||
value: "APPLY",
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
unitPrice,
|
||||
serviceTypeText(type) {
|
||||
const map = {
|
||||
RETURN_MONEY: "退款",
|
||||
RETURN_GOODS: "退货",
|
||||
EXCHANGE_GOODS: "换货",
|
||||
};
|
||||
return map[type] || type || "-";
|
||||
{
|
||||
title: `通过售后${afterSaleNumData.value.passNum ? "(" + afterSaleNumData.value.passNum + ")" : ""}`,
|
||||
value: "PASS",
|
||||
},
|
||||
serviceTypeTagType(type) {
|
||||
const map = {
|
||||
RETURN_MONEY: "primary",
|
||||
RETURN_GOODS: "warning",
|
||||
EXCHANGE_GOODS: "success",
|
||||
};
|
||||
return map[type] || "info";
|
||||
{
|
||||
title: `拒绝售后${afterSaleNumData.value.refuseNum ? "(" + afterSaleNumData.value.refuseNum + ")" : ""}`,
|
||||
value: "REFUSE",
|
||||
},
|
||||
serviceStatusText(status) {
|
||||
const map = {
|
||||
APPLY: "申请中",
|
||||
PASS: "通过售后",
|
||||
REFUSE: "拒绝售后",
|
||||
BUYER_RETURN: "买家退货,待卖家收货",
|
||||
SELLER_CONFIRM: "卖家确认收货",
|
||||
SELLER_TERMINATION: "卖家终止售后",
|
||||
BUYER_CANCEL: "买家取消售后",
|
||||
COMPLETE: "完成售后",
|
||||
WAIT_REFUND: "待平台退款",
|
||||
};
|
||||
return map[status] || status || "-";
|
||||
{
|
||||
title: `待收货${afterSaleNumData.value.buyerReturnNum ? "(" + afterSaleNumData.value.buyerReturnNum + ")" : ""}`,
|
||||
value: "BUYER_RETURN",
|
||||
},
|
||||
serviceStatusTagType(status) {
|
||||
const map = {
|
||||
APPLY: "primary",
|
||||
PASS: "info",
|
||||
REFUSE: "warning",
|
||||
BUYER_RETURN: "warning",
|
||||
SELLER_CONFIRM: "",
|
||||
SELLER_TERMINATION: "success",
|
||||
BUYER_CANCEL: "danger",
|
||||
COMPLETE: "success",
|
||||
WAIT_REFUND: "primary",
|
||||
};
|
||||
return map[status] || "info";
|
||||
{
|
||||
title: `确认收货${afterSaleNumData.value.sellerConfirmNum ? "(" + afterSaleNumData.value.sellerConfirmNum + ")" : ""}`,
|
||||
value: "SELLER_CONFIRM",
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
this.getAfterSaleNumData();
|
||||
{
|
||||
title: `完成售后${afterSaleNumData.value.completeNum ? "(" + afterSaleNumData.value.completeNum + ")" : ""}`,
|
||||
value: "COMPLETE",
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
{
|
||||
title: `卖家终止售后${afterSaleNumData.value.sellerTerminationNum ? "(" + afterSaleNumData.value.sellerTerminationNum + ")" : ""}`,
|
||||
value: "SELLER_TERMINATION",
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
{
|
||||
title: `买家取消售后${afterSaleNumData.value.buyerCancelNum ? "(" + afterSaleNumData.value.buyerCancelNum + ")" : ""}`,
|
||||
value: "BUYER_CANCEL",
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
this.getAfterSaleNumData();
|
||||
{
|
||||
title: `等待平台退款${afterSaleNumData.value.waitRefundNum ? "(" + afterSaleNumData.value.waitRefundNum + ")" : ""}`,
|
||||
value: "WAIT_REFUND",
|
||||
},
|
||||
selectDateRange(v) {
|
||||
if (v) {
|
||||
this.searchForm.startDate = v[0];
|
||||
this.searchForm.endDate = v[1];
|
||||
} else {
|
||||
this.searchForm.startDate = "";
|
||||
this.searchForm.endDate = "";
|
||||
}
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
API_Order.getAfterSaleOrderPage(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
getAfterSaleNumData() {
|
||||
const { serviceStatus, ...searchParams } = this.searchForm;
|
||||
API_Order.getAfterSaleNumVO(searchParams).then((res) => {
|
||||
if (res.success) {
|
||||
this.afterSaleNumData = res.result;
|
||||
}
|
||||
});
|
||||
},
|
||||
detail(v) {
|
||||
const sn = v.sn;
|
||||
customRouterPush({
|
||||
name: "after-order-detail",
|
||||
query: { sn: sn },
|
||||
});
|
||||
},
|
||||
onStatusTabClick(tab) {
|
||||
this.serviceStatusClick(tab.paneName);
|
||||
},
|
||||
serviceStatusClick(item) {
|
||||
this.currentStatus = item;
|
||||
if (item === "" || item === undefined) {
|
||||
delete this.searchForm.serviceStatus;
|
||||
} else {
|
||||
this.searchForm.serviceStatus = item;
|
||||
}
|
||||
this.getDataList();
|
||||
this.getAfterSaleNumData();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
];
|
||||
});
|
||||
|
||||
function serviceTypeText(type) {
|
||||
const map = {
|
||||
RETURN_MONEY: "退款",
|
||||
RETURN_GOODS: "退货",
|
||||
EXCHANGE_GOODS: "换货",
|
||||
};
|
||||
return map[type] || type || "-";
|
||||
}
|
||||
function serviceTypeTagType(type) {
|
||||
const map = {
|
||||
RETURN_MONEY: "primary",
|
||||
RETURN_GOODS: "warning",
|
||||
EXCHANGE_GOODS: "success",
|
||||
};
|
||||
return map[type] || "info";
|
||||
}
|
||||
function serviceStatusText(status) {
|
||||
const map = {
|
||||
APPLY: "申请中",
|
||||
PASS: "通过售后",
|
||||
REFUSE: "拒绝售后",
|
||||
BUYER_RETURN: "买家退货,待卖家收货",
|
||||
SELLER_CONFIRM: "卖家确认收货",
|
||||
SELLER_TERMINATION: "卖家终止售后",
|
||||
BUYER_CANCEL: "买家取消售后",
|
||||
COMPLETE: "完成售后",
|
||||
WAIT_REFUND: "待平台退款",
|
||||
};
|
||||
return map[status] || status || "-";
|
||||
}
|
||||
function serviceStatusTagType(status) {
|
||||
const map = {
|
||||
APPLY: "primary",
|
||||
PASS: "info",
|
||||
REFUSE: "warning",
|
||||
BUYER_RETURN: "warning",
|
||||
SELLER_CONFIRM: "",
|
||||
SELLER_TERMINATION: "success",
|
||||
BUYER_CANCEL: "danger",
|
||||
COMPLETE: "success",
|
||||
WAIT_REFUND: "primary",
|
||||
};
|
||||
return map[status] || "info";
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
getAfterSaleNumData();
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
getAfterSaleNumData();
|
||||
}
|
||||
function selectDateRange(v) {
|
||||
if (v) {
|
||||
searchForm.startDate = v[0];
|
||||
searchForm.endDate = v[1];
|
||||
} else {
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.getAfterSaleOrderPage(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function getAfterSaleNumData() {
|
||||
const { serviceStatus, ...searchParams } = searchForm;
|
||||
API_Order.getAfterSaleNumVO(searchParams).then((res) => {
|
||||
if (res.success) {
|
||||
afterSaleNumData.value = res.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
function detail(v) {
|
||||
const sn = v.sn;
|
||||
customRouterPush({
|
||||
name: "after-order-detail",
|
||||
query: { sn: sn },
|
||||
});
|
||||
}
|
||||
function onStatusTabClick(tab) {
|
||||
serviceStatusClick(tab.paneName);
|
||||
}
|
||||
function serviceStatusClick(item) {
|
||||
currentStatus.value = item;
|
||||
if (item === "" || item === undefined) {
|
||||
delete searchForm.serviceStatus;
|
||||
} else {
|
||||
searchForm.serviceStatus = item;
|
||||
}
|
||||
getDataList();
|
||||
getAfterSaleNumData();
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -332,185 +332,184 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import * as API_Order from "@/api/order";
|
||||
import vueQr from "vue-qr";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
export default {
|
||||
components: {
|
||||
"vue-qr": vueQr,
|
||||
},
|
||||
name: "orderDetail",
|
||||
data() {
|
||||
return {
|
||||
sn: "", // 订单号
|
||||
logisticsModal: false, //查询物流模态框
|
||||
|
||||
logisticsInfo: {}, //物流信息
|
||||
afterSaleInfo: {}, // 售后信息
|
||||
afterSaleImage: [], //会员申诉图片
|
||||
submitLoading: false, // 添加或编辑提交状态
|
||||
storeMsg: {}, // 商家地址信息
|
||||
//商家处理意见
|
||||
params: {
|
||||
serviceStatus: "PASS",
|
||||
remark: "",
|
||||
},
|
||||
//平台退款
|
||||
refundPriceForm: {
|
||||
remark: "",
|
||||
},
|
||||
afterSaleStatusList: [
|
||||
// 售后状态列表
|
||||
{
|
||||
name: "申请中",
|
||||
status: "APPLY",
|
||||
},
|
||||
{
|
||||
name: "通过售后",
|
||||
status: "PASS",
|
||||
},
|
||||
{
|
||||
name: "拒绝售后",
|
||||
status: "REFUSE",
|
||||
},
|
||||
{
|
||||
name: "买家退货,待卖家收货",
|
||||
status: "BUYER_RETURN",
|
||||
},
|
||||
{
|
||||
name: "卖家确认收货",
|
||||
status: "SELLER_CONFIRM",
|
||||
},
|
||||
{
|
||||
name: "卖家终止售后",
|
||||
status: "SELLER_TERMINATION",
|
||||
},
|
||||
{
|
||||
name: "买家取消售后",
|
||||
status: "BUYER_CANCEL",
|
||||
},
|
||||
{
|
||||
name: "完成售后",
|
||||
status: "COMPLETE",
|
||||
},
|
||||
{
|
||||
name: "待平台退款",
|
||||
status: "WAIT_REFUND",
|
||||
},
|
||||
],
|
||||
};
|
||||
defineOptions({ name: "orderDetail" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const sn = ref(""); // 订单号
|
||||
const logisticsModal = ref(false); //查询物流模态框
|
||||
|
||||
const logisticsInfo = ref({}); //物流信息
|
||||
const afterSaleInfo = ref({}); // 售后信息
|
||||
const afterSaleImage = ref([]); //会员申诉图片
|
||||
const submitLoading = ref(false); // 添加或编辑提交状态
|
||||
const storeMsg = ref({}); // 商家地址信息
|
||||
// 原代码未在 data 声明、直接挂 this 上的字段,补声明避免 ReferenceError
|
||||
let loading = false;
|
||||
//商家处理意见
|
||||
const params = reactive({
|
||||
serviceStatus: "PASS",
|
||||
remark: "",
|
||||
});
|
||||
//平台退款
|
||||
const refundPriceForm = reactive({
|
||||
remark: "",
|
||||
});
|
||||
const afterSaleStatusList = ref([
|
||||
// 售后状态列表
|
||||
{
|
||||
name: "申请中",
|
||||
status: "APPLY",
|
||||
},
|
||||
methods: {
|
||||
// 获取售后详情
|
||||
getDetail() {
|
||||
this.loading = true;
|
||||
API_Order.getAfterSaleOrderDetail(this.sn).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.afterSaleInfo = res.result;
|
||||
this.afterSaleInfo.showDelivery = this.showDelivery(
|
||||
this.afterSaleInfo.serviceStatus
|
||||
);
|
||||
this.afterSaleInfo.serviceName = this.filterOrderStatus(
|
||||
this.afterSaleInfo.serviceStatus
|
||||
);
|
||||
this.afterSaleImage = (res.result.afterSaleImage || "").split(",");
|
||||
//退货地址去掉逗号
|
||||
if (this.afterSaleInfo.mconsigneeAddressPath)
|
||||
this.afterSaleInfo.mconsigneeAddressPath = this.afterSaleInfo.mconsigneeAddressPath.replaceAll(
|
||||
{
|
||||
name: "通过售后",
|
||||
status: "PASS",
|
||||
},
|
||||
{
|
||||
name: "拒绝售后",
|
||||
status: "REFUSE",
|
||||
},
|
||||
{
|
||||
name: "买家退货,待卖家收货",
|
||||
status: "BUYER_RETURN",
|
||||
},
|
||||
{
|
||||
name: "卖家确认收货",
|
||||
status: "SELLER_CONFIRM",
|
||||
},
|
||||
{
|
||||
name: "卖家终止售后",
|
||||
status: "SELLER_TERMINATION",
|
||||
},
|
||||
{
|
||||
name: "买家取消售后",
|
||||
status: "BUYER_CANCEL",
|
||||
},
|
||||
{
|
||||
name: "完成售后",
|
||||
status: "COMPLETE",
|
||||
},
|
||||
{
|
||||
name: "待平台退款",
|
||||
status: "WAIT_REFUND",
|
||||
},
|
||||
]);
|
||||
|
||||
// 获取售后详情
|
||||
function getDetail() {
|
||||
loading = true;
|
||||
API_Order.getAfterSaleOrderDetail(sn.value).then((res) => {
|
||||
loading = false;
|
||||
if (res.success) {
|
||||
afterSaleInfo.value = res.result;
|
||||
afterSaleInfo.value.showDelivery = showDelivery(
|
||||
afterSaleInfo.value.serviceStatus
|
||||
);
|
||||
afterSaleInfo.value.serviceName = filterOrderStatus(
|
||||
afterSaleInfo.value.serviceStatus
|
||||
);
|
||||
afterSaleImage.value = (res.result.afterSaleImage || "").split(",");
|
||||
//退货地址去掉逗号
|
||||
if (afterSaleInfo.value.mconsigneeAddressPath)
|
||||
afterSaleInfo.value.mconsigneeAddressPath = afterSaleInfo.value.mconsigneeAddressPath.replaceAll(
|
||||
",",
|
||||
" "
|
||||
);
|
||||
|
||||
params.actualRefundPrice = afterSaleInfo.value.applyRefundPrice;
|
||||
// 如果显示物流信息,展示商家地址
|
||||
if (afterSaleInfo.value.showDelivery) {
|
||||
API_Order.storeAddress(sn.value).then((resu) => {
|
||||
if (resu.success) {
|
||||
const obj = resu.result;
|
||||
obj.salesConsigneeAddressPath = obj.salesConsigneeAddressPath.replaceAll(
|
||||
",",
|
||||
" "
|
||||
""
|
||||
);
|
||||
|
||||
this.params.actualRefundPrice = this.afterSaleInfo.applyRefundPrice;
|
||||
// 如果显示物流信息,展示商家地址
|
||||
if (this.afterSaleInfo.showDelivery) {
|
||||
API_Order.storeAddress(this.sn).then((resu) => {
|
||||
if (resu.success) {
|
||||
const obj = resu.result;
|
||||
obj.salesConsigneeAddressPath = obj.salesConsigneeAddressPath.replaceAll(
|
||||
",",
|
||||
""
|
||||
);
|
||||
this.storeMsg = obj;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
//平台退款
|
||||
refundPriceSubmit() {
|
||||
if (this.refundPriceForm.remark == "") {
|
||||
ElMessage.error("请输入退款备注");
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm("请确认退款?", "确认退款", { type: "warning" }).then(() => {
|
||||
API_Order.refundPrice(this.sn, this.refundPriceForm).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("收款成功");
|
||||
this.getDetail();
|
||||
storeMsg.value = obj;
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
//查询物流
|
||||
logisticsSeller() {
|
||||
this.logisticsModal = true;
|
||||
API_Order.getAfterSaleTraces(this.sn).then((res) => {
|
||||
if (res.success && res.result != null) {
|
||||
this.logisticsInfo = res.result;
|
||||
}
|
||||
});
|
||||
},
|
||||
//关闭物流弹出框
|
||||
logisticsClose() {
|
||||
this.logisticsModal = false;
|
||||
},
|
||||
//回复
|
||||
handleSubmit() {
|
||||
if (this.params.remark == "") {
|
||||
ElMessage.error("请输入备注信息");
|
||||
return;
|
||||
}
|
||||
if (this.params.actualRefundPrice == "") {
|
||||
ElMessage.error("请输入退款金额");
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
//平台退款
|
||||
function refundPriceSubmit() {
|
||||
if (refundPriceForm.remark == "") {
|
||||
ElMessage.error("请输入退款备注");
|
||||
return;
|
||||
}
|
||||
ElMessageBox.confirm("请确认退款?", "确认退款", { type: "warning" }).then(() => {
|
||||
API_Order.refundPrice(sn.value, refundPriceForm).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("收款成功");
|
||||
getDetail();
|
||||
}
|
||||
API_Order.afterSaleSellerReview(this.sn, this.params).then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("审核成功");
|
||||
this.params.remark = "";
|
||||
this.getDetail();
|
||||
}
|
||||
});
|
||||
},
|
||||
filterOrderStatus(status) {
|
||||
// 获取订单状态中文
|
||||
const ob = this.afterSaleStatusList.filter((e) => {
|
||||
return e.status === status;
|
||||
});
|
||||
return ob.length > 0 ? ob[0].name : status;
|
||||
},
|
||||
// 根据订单状态判断是否显示物流信息
|
||||
showDelivery(status) {
|
||||
let flag = false;
|
||||
this.afterSaleStatusList.forEach((e, index) => {
|
||||
// 订单为买家退货,待卖家收货之后的状态,并且不是买家取消售后,展示物流信息
|
||||
if (e.status === status && index >= 3 && index !== 6) {
|
||||
flag = true;
|
||||
}
|
||||
});
|
||||
return flag;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.sn = this.$route.query.sn;
|
||||
this.getDetail();
|
||||
},
|
||||
};
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
//查询物流
|
||||
function logisticsSeller() {
|
||||
logisticsModal.value = true;
|
||||
API_Order.getAfterSaleTraces(sn.value).then((res) => {
|
||||
if (res.success && res.result != null) {
|
||||
logisticsInfo.value = res.result;
|
||||
}
|
||||
});
|
||||
}
|
||||
//关闭物流弹出框
|
||||
function logisticsClose() {
|
||||
logisticsModal.value = false;
|
||||
}
|
||||
//回复
|
||||
function handleSubmit() {
|
||||
if (params.remark == "") {
|
||||
ElMessage.error("请输入备注信息");
|
||||
return;
|
||||
}
|
||||
if (params.actualRefundPrice == "") {
|
||||
ElMessage.error("请输入退款金额");
|
||||
return;
|
||||
}
|
||||
API_Order.afterSaleSellerReview(sn.value, params).then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("审核成功");
|
||||
params.remark = "";
|
||||
getDetail();
|
||||
}
|
||||
});
|
||||
}
|
||||
function filterOrderStatus(status) {
|
||||
// 获取订单状态中文
|
||||
const ob = afterSaleStatusList.value.filter((e) => {
|
||||
return e.status === status;
|
||||
});
|
||||
return ob.length > 0 ? ob[0].name : status;
|
||||
}
|
||||
// 根据订单状态判断是否显示物流信息
|
||||
function showDelivery(status) {
|
||||
let flag = false;
|
||||
afterSaleStatusList.value.forEach((e, index) => {
|
||||
// 订单为买家退货,待卖家收货之后的状态,并且不是买家取消售后,展示物流信息
|
||||
if (e.status === status && index >= 3 && index !== 6) {
|
||||
flag = true;
|
||||
}
|
||||
});
|
||||
return flag;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
sn.value = route.query.sn;
|
||||
getDetail();
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.main-content {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -104,97 +104,100 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import * as API_Order from "@/api/order";
|
||||
import vueQr from "vue-qr";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "orderComplaint",
|
||||
components: { vueQr },
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
},
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.getDataList();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
complainStatusText(v) {
|
||||
const map = {
|
||||
NEW: "新投诉",
|
||||
CANCEL: "已撤销",
|
||||
WAIT_APPEAL: "待申诉",
|
||||
COMMUNICATION: "对话中",
|
||||
WAIT_ARBITRATION: "等待仲裁",
|
||||
COMPLETE: "已完成",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
},
|
||||
complainStatusTagType(v) {
|
||||
const map = {
|
||||
NEW: "primary",
|
||||
CANCEL: "info",
|
||||
WAIT_APPEAL: "warning",
|
||||
COMMUNICATION: "warning",
|
||||
WAIT_ARBITRATION: "",
|
||||
COMPLETE: "success",
|
||||
};
|
||||
return map[v] || "info";
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
API_Order.getOrderComplain(this.searchForm)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
detail(v) {
|
||||
customRouterPush({
|
||||
name: "order-complaint-detail",
|
||||
query: { id: v.id },
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "orderComplaint" });
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
const table = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
});
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
watch(
|
||||
() => route.query,
|
||||
() => {
|
||||
getDataList();
|
||||
}
|
||||
);
|
||||
|
||||
function complainStatusText(v) {
|
||||
const map = {
|
||||
NEW: "新投诉",
|
||||
CANCEL: "已撤销",
|
||||
WAIT_APPEAL: "待申诉",
|
||||
COMMUNICATION: "对话中",
|
||||
WAIT_ARBITRATION: "等待仲裁",
|
||||
COMPLETE: "已完成",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
}
|
||||
function complainStatusTagType(v) {
|
||||
const map = {
|
||||
NEW: "primary",
|
||||
CANCEL: "info",
|
||||
WAIT_APPEAL: "warning",
|
||||
COMMUNICATION: "warning",
|
||||
WAIT_ARBITRATION: "",
|
||||
COMPLETE: "success",
|
||||
};
|
||||
return map[v] || "info";
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.getOrderComplain(searchForm)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function detail(v) {
|
||||
customRouterPush({
|
||||
name: "order-complaint-detail",
|
||||
query: { id: v.id },
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -203,140 +203,141 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import * as API_Order from "@/api/order";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { unitPrice } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "orderComplaintDetail" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const emptyComplaint = () => ({
|
||||
id: "",
|
||||
orderComplaintCommunications: [],
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "orderComplaintDetail",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
id: "",
|
||||
complaintInfo: emptyComplaint(),
|
||||
images: [],
|
||||
appealImages: [],
|
||||
submitLoading: false,
|
||||
params: {
|
||||
content: "",
|
||||
complainId: "",
|
||||
},
|
||||
arbitrationParams: {
|
||||
arbitrationResult: "",
|
||||
},
|
||||
arbitrationResultShow: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
unitPrice,
|
||||
complainStatusText(v) {
|
||||
const map = {
|
||||
NEW: "新投诉",
|
||||
CANCEL: "已撤销",
|
||||
WAIT_APPEAL: "待申诉",
|
||||
COMMUNICATION: "对话中",
|
||||
WAIT_ARBITRATION: "等待仲裁",
|
||||
COMPLETE: "已完成",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
},
|
||||
handleStoreComplaint() {
|
||||
API_Order.storeComplain({
|
||||
complainStatus: "WAIT_APPEAL",
|
||||
complainId: this.complaintInfo.id,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
this.getDetail();
|
||||
}
|
||||
});
|
||||
},
|
||||
getDetail() {
|
||||
if (!this.id) return;
|
||||
this.loading = true;
|
||||
API_Order.getOrderComplainDetail(this.id)
|
||||
.then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.complaintInfo = res.result || emptyComplaint();
|
||||
this.images = (res.result.images || "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
this.appealImages = (res.result.appealImages || "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
returnDataList() {
|
||||
this.$router.push({ name: "orderComplaint" });
|
||||
},
|
||||
arbitrationHandle() {
|
||||
this.arbitrationResultShow = true;
|
||||
},
|
||||
arbitrationHandleSubmit() {
|
||||
if (!this.arbitrationParams.arbitrationResult) {
|
||||
ElMessage.error("请填写仲裁内容");
|
||||
return;
|
||||
const loading = ref(false);
|
||||
const id = ref("");
|
||||
const complaintInfo = ref(emptyComplaint());
|
||||
const images = ref([]);
|
||||
const appealImages = ref([]);
|
||||
const submitLoading = ref(false);
|
||||
const params = reactive({
|
||||
content: "",
|
||||
complainId: "",
|
||||
});
|
||||
const arbitrationParams = reactive({
|
||||
arbitrationResult: "",
|
||||
});
|
||||
const arbitrationResultShow = ref(false);
|
||||
|
||||
function complainStatusText(v) {
|
||||
const map = {
|
||||
NEW: "新投诉",
|
||||
CANCEL: "已撤销",
|
||||
WAIT_APPEAL: "待申诉",
|
||||
COMMUNICATION: "对话中",
|
||||
WAIT_ARBITRATION: "等待仲裁",
|
||||
COMPLETE: "已完成",
|
||||
};
|
||||
return map[v] || v || "-";
|
||||
}
|
||||
function handleStoreComplaint() {
|
||||
API_Order.storeComplain({
|
||||
complainStatus: "WAIT_APPEAL",
|
||||
complainId: complaintInfo.value.id,
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("操作成功");
|
||||
getDetail();
|
||||
}
|
||||
});
|
||||
}
|
||||
function getDetail() {
|
||||
if (!id.value) return;
|
||||
loading.value = true;
|
||||
API_Order.getOrderComplainDetail(id.value)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
complaintInfo.value = res.result || emptyComplaint();
|
||||
images.value = (res.result.images || "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
appealImages.value = (res.result.appealImages || "")
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
this.submitLoading = true;
|
||||
API_Order.orderComplete(this.id, this.arbitrationParams)
|
||||
.then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("仲裁成功");
|
||||
this.arbitrationParams.arbitrationResult = "";
|
||||
this.arbitrationResultShow = false;
|
||||
this.getDetail();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.submitLoading = false;
|
||||
});
|
||||
},
|
||||
handleSubmit() {
|
||||
if (!this.params.content) {
|
||||
ElMessage.error("请填写对话内容");
|
||||
return;
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function returnDataList() {
|
||||
router.push({ name: "orderComplaint" });
|
||||
}
|
||||
function arbitrationHandle() {
|
||||
arbitrationResultShow.value = true;
|
||||
}
|
||||
function arbitrationHandleSubmit() {
|
||||
if (!arbitrationParams.arbitrationResult) {
|
||||
ElMessage.error("请填写仲裁内容");
|
||||
return;
|
||||
}
|
||||
submitLoading.value = true;
|
||||
API_Order.orderComplete(id.value, arbitrationParams)
|
||||
.then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("仲裁成功");
|
||||
arbitrationParams.arbitrationResult = "";
|
||||
arbitrationResultShow.value = false;
|
||||
getDetail();
|
||||
}
|
||||
this.submitLoading = true;
|
||||
this.params.complainId = this.id;
|
||||
API_Order.addOrderCommunication(this.params)
|
||||
.then((res) => {
|
||||
this.submitLoading = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("对话成功");
|
||||
this.params.content = "";
|
||||
this.getDetail();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.submitLoading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.id = this.$route.query.id;
|
||||
this.getDetail();
|
||||
},
|
||||
watch: {
|
||||
"$route.query.id"(val) {
|
||||
this.id = val;
|
||||
this.getDetail();
|
||||
},
|
||||
},
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
function handleSubmit() {
|
||||
if (!params.content) {
|
||||
ElMessage.error("请填写对话内容");
|
||||
return;
|
||||
}
|
||||
submitLoading.value = true;
|
||||
params.complainId = id.value;
|
||||
API_Order.addOrderCommunication(params)
|
||||
.then((res) => {
|
||||
submitLoading.value = false;
|
||||
if (res.success) {
|
||||
ElMessage.success("对话成功");
|
||||
params.content = "";
|
||||
getDetail();
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
submitLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
id.value = route.query.id;
|
||||
getDetail();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.query.id,
|
||||
(val) => {
|
||||
id.value = val;
|
||||
getDetail();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -111,62 +111,60 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Order from "@/api/order";
|
||||
|
||||
export default {
|
||||
name: "paymentLog",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
sn: "",
|
||||
payStatus: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
},
|
||||
times: [],
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
changeDate(val) {
|
||||
this.searchForm.startDate = val[0];
|
||||
this.searchForm.endDate = val[1];
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
API_Order.paymentLog(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "paymentLog" });
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
sn: "",
|
||||
payStatus: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
});
|
||||
const times = ref([]);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function changeDate(val) {
|
||||
searchForm.startDate = val[0];
|
||||
searchForm.endDate = val[1];
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.paymentLog(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -108,71 +108,68 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Order from "@/api/order";
|
||||
import { unitPrice } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "refundLog",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderSn: "",
|
||||
isRefund: "",
|
||||
},
|
||||
selectDate: null,
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
unitPrice,
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
selectDateRange(v) {
|
||||
if (v && v[0] && v[1]) {
|
||||
this.searchForm.startDate = v[0];
|
||||
this.searchForm.endDate = v[1];
|
||||
} else {
|
||||
this.searchForm.startDate = "";
|
||||
this.searchForm.endDate = "";
|
||||
}
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
API_Order.refundLog(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "refundLog" });
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderSn: "",
|
||||
isRefund: "",
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function selectDateRange(v) {
|
||||
if (v && v[0] && v[1]) {
|
||||
searchForm.startDate = v[0];
|
||||
searchForm.endDate = v[1];
|
||||
} else {
|
||||
searchForm.startDate = "";
|
||||
searchForm.endDate = "";
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.refundLog(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
inline
|
||||
label-width="70px"
|
||||
@@ -57,7 +57,7 @@
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
ref="tableRef"
|
||||
v-loading="loading"
|
||||
border
|
||||
:data="data"
|
||||
@@ -126,107 +126,106 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { onMounted, reactive, ref } from "vue";
|
||||
import * as API_Order from "@/api/order";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { customRouterPush } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "fictitiousOrderList",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "",
|
||||
order: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderType: "VIRTUAL",
|
||||
orderSn: "",
|
||||
buyerName: "",
|
||||
orderStatus: "",
|
||||
},
|
||||
selectDate: null,
|
||||
data: [],
|
||||
total: 0,
|
||||
orderStatus: [
|
||||
{ title: "全部", value: "" },
|
||||
{ title: "未付款", value: "UNPAID" },
|
||||
{ title: "已付款", value: "PAID" },
|
||||
{ title: "待核验", value: "TAKE" },
|
||||
{ title: "已完成", value: "COMPLETED" },
|
||||
{ title: "已关闭", value: "CANCELLED" },
|
||||
],
|
||||
currentStatus: "",
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePage() {
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = 20;
|
||||
this.getDataList();
|
||||
},
|
||||
changeSort(e) {
|
||||
this.searchForm.sort = e.prop;
|
||||
this.searchForm.order = e.order === "ascending" ? "asc" : e.order === "descending" ? "desc" : "";
|
||||
this.getDataList();
|
||||
},
|
||||
selectDateRange(v) {
|
||||
if (v) {
|
||||
this.searchForm.startDate = v[0];
|
||||
this.searchForm.endDate = v[1];
|
||||
defineOptions({ name: "fictitiousOrderList" });
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
const tableRef = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "",
|
||||
order: "",
|
||||
startDate: "",
|
||||
endDate: "",
|
||||
orderType: "VIRTUAL",
|
||||
orderSn: "",
|
||||
buyerName: "",
|
||||
orderStatus: "",
|
||||
});
|
||||
const selectDate = ref(null);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const orderStatus = ref([
|
||||
{ title: "全部", value: "" },
|
||||
{ title: "未付款", value: "UNPAID" },
|
||||
{ title: "已付款", value: "PAID" },
|
||||
{ title: "待核验", value: "TAKE" },
|
||||
{ title: "已完成", value: "COMPLETED" },
|
||||
{ title: "已关闭", value: "CANCELLED" },
|
||||
]);
|
||||
const currentStatus = ref("");
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
function init() {
|
||||
getDataList();
|
||||
}
|
||||
function changePage() {
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize() {
|
||||
searchForm.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = 20;
|
||||
getDataList();
|
||||
}
|
||||
function changeSort(e) {
|
||||
searchForm.sort = e.prop;
|
||||
searchForm.order = e.order === "ascending" ? "asc" : e.order === "descending" ? "desc" : "";
|
||||
getDataList();
|
||||
}
|
||||
function selectDateRange(v) {
|
||||
if (v) {
|
||||
searchForm.startDate = v[0];
|
||||
searchForm.endDate = v[1];
|
||||
}
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
API_Order.getOrderList(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
});
|
||||
}
|
||||
function confirmPrice(v) {
|
||||
ElMessageBox.confirm("您确定要收款吗?", "确认收款", { type: "warning" }).then(() => {
|
||||
API_Order.orderPay(v.sn).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("收款成功");
|
||||
getDataList();
|
||||
}
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
API_Order.getOrderList(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
});
|
||||
},
|
||||
confirmPrice(v) {
|
||||
ElMessageBox.confirm("您确定要收款吗?", "确认收款", { type: "warning" }).then(() => {
|
||||
API_Order.orderPay(v.sn).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("收款成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
detail(v) {
|
||||
let sn = v.sn;
|
||||
customRouterPush({
|
||||
name: "order-detail",
|
||||
query: { sn: sn, orderType: v.orderType },
|
||||
});
|
||||
},
|
||||
onStatusTabClick(tab) {
|
||||
const item = tab.paneName;
|
||||
this.currentStatus = item;
|
||||
this.searchForm.orderStatus = item;
|
||||
this.getDataList();
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
function detail(v) {
|
||||
let sn = v.sn;
|
||||
customRouterPush({
|
||||
name: "order-detail",
|
||||
query: { sn: sn, orderType: v.orderType },
|
||||
});
|
||||
}
|
||||
function onStatusTabClick(tab) {
|
||||
const item = tab.paneName;
|
||||
currentStatus.value = item;
|
||||
searchForm.orderStatus = item;
|
||||
getDataList();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -357,7 +357,7 @@
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="modal" title="修改金额" width="530px">
|
||||
<el-form ref="modifyPriceForm" :model="modifyPriceForm" label-width="70px" :rules="modifyPriceValidate" @submit.prevent>
|
||||
<el-form ref="modifyPriceFormRef" :model="modifyPriceForm" label-width="70px" :rules="modifyPriceValidate" @submit.prevent>
|
||||
<el-form-item label="订单金额" prop="price">
|
||||
<el-input-number v-model="modifyPriceForm.price" :min="0" :max="999999" style="width: 120px" />
|
||||
<span class="ml_10">元</span>
|
||||
@@ -370,7 +370,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="orderCancelModal" title="订单取消" width="530px">
|
||||
<el-form ref="orderCancelForm" :model="orderCancelForm" label-width="100px" :rules="orderCancelValidate">
|
||||
<el-form ref="orderCancelFormRef" :model="orderCancelForm" label-width="100px" :rules="orderCancelValidate">
|
||||
<el-form-item label="取消原因" prop="reason">
|
||||
<el-input v-model="orderCancelForm.reason" type="textarea" :rows="3" placeholder="请输入取消原因" />
|
||||
</el-form-item>
|
||||
@@ -382,7 +382,7 @@
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="addressModal" title="修改收件信息" width="530px">
|
||||
<el-form ref="addressForm" :model="addressForm" label-width="100px" :rules="addressRule">
|
||||
<el-form ref="addressFormRef" :model="addressForm" label-width="100px" :rules="addressRule">
|
||||
<el-form-item label="收件人" prop="consigneeName">
|
||||
<el-input v-model="addressForm.consigneeName" maxlength="20" />
|
||||
</el-form-item>
|
||||
@@ -518,7 +518,9 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import * as API_Order from "@/api/order";
|
||||
import { platformRefund, noShowRefund, getAppointmentOrderDetail } from "@/api/appointment";
|
||||
import * as RegExp from "@/libs/RegExp.js";
|
||||
@@ -529,457 +531,456 @@ import { printElement } from "@/utils/print";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { unitPrice, clientTypeWay } from "@/utils/filters";
|
||||
import { formatCardKeyStatus } from "@/constants/cardKey";
|
||||
export default {
|
||||
name: "orderList",
|
||||
components: {
|
||||
|
||||
multipleMap,
|
||||
"vue-qr": vueQr,
|
||||
defineOptions({ name: "orderList" });
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// 模板 ref(与表单 data 同名的改为 xxxRef,避免互相覆盖)
|
||||
const modifyPriceFormRef = ref(null);
|
||||
const orderCancelFormRef = ref(null);
|
||||
const addressFormRef = ref(null);
|
||||
const map = ref(null);
|
||||
|
||||
const typeList = ref([]);
|
||||
const showPrices = ref(false);
|
||||
const printHiddenFlag = ref(false);//隐藏信息
|
||||
const loading = ref(false); //加载表格
|
||||
|
||||
const addr = ref(""); //地区
|
||||
let regionId = ""; // 原代码未在 data 声明直接挂 this 的历史遗留字段
|
||||
|
||||
|
||||
const orderLogInfo = ref([]); //订单日志数据
|
||||
const orderLogModal = ref(false); //弹出调整价格框
|
||||
const checkedLogistics = ref([]); //选中的物流公司集合
|
||||
const allowOperation = ref({}); //订单可才做选项
|
||||
const sn = ref(""); //订单编号
|
||||
const orderInfo = ref({
|
||||
order: {
|
||||
priceDetailDTO: {},
|
||||
},
|
||||
computed: {
|
||||
/** 电子卡券订单:无物流区,卡密来自 orderItems[].cardKeys */
|
||||
isECouponOrder() {
|
||||
const t = this.orderInfo?.order?.orderType;
|
||||
return t === "E_COUPON" || this.$route.query.orderType === "E_COUPON";
|
||||
});
|
||||
const appointmentExt = ref({});
|
||||
const modal = ref(false); //弹出调整价格框
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1, // 当前页数
|
||||
pageSize: 100, // 页面大小
|
||||
orderSn: "", //订单sn
|
||||
});
|
||||
//调整价格表单
|
||||
const modifyPriceForm = reactive({
|
||||
price: 0,
|
||||
});
|
||||
//订单取消表单
|
||||
const orderCancelForm = reactive({
|
||||
reason: "",
|
||||
});
|
||||
//弹出订单取消框
|
||||
const orderCancelModal = ref(false);
|
||||
//订单发货
|
||||
const orderDeliveryForm = reactive({
|
||||
logisticsNo: "", //发货单号
|
||||
logisticsId: "", //物流公司
|
||||
});
|
||||
//验证要调整的订单金额
|
||||
const modifyPriceValidate = {
|
||||
reason: [
|
||||
{ required: true, message: "请输入大于0小于99999的合法金额" },
|
||||
{
|
||||
pattern: /^[1-9]\d{0,3}(\.\d{1,2})?$/,
|
||||
message: "请输入大于0小于9999的合法金额",
|
||||
trigger: "change",
|
||||
},
|
||||
isVirtualOrder() {
|
||||
const t = this.orderInfo?.order?.orderType;
|
||||
return t === "VIRTUAL" || this.$route.query.orderType === "VIRTUAL";
|
||||
},
|
||||
isNonPhysicalOrder() {
|
||||
return this.isVirtualOrder || this.isECouponOrder || this.isAppointmentOrder;
|
||||
},
|
||||
isAppointmentOrder() {
|
||||
const t = this.orderInfo?.order?.orderType;
|
||||
return t === "APPOINTMENT" || this.$route.query.orderType === "APPOINTMENT";
|
||||
},
|
||||
appointmentSlotText() {
|
||||
const v = this.appointmentExt || {};
|
||||
if (!v.slotDate) return "";
|
||||
const date = String(v.slotDate).slice(0, 10);
|
||||
const slot = v.slotStartTime && v.slotEndTime ? `${v.slotStartTime}-${v.slotEndTime}` : "";
|
||||
return [date, slot].filter(Boolean).join(" ");
|
||||
},
|
||||
appointmentFormGroups() {
|
||||
return this.parseFormAnswerGroups(this.appointmentExt?.formAnswerJson);
|
||||
},
|
||||
canPlatformRefund() {
|
||||
const order = this.orderInfo?.order;
|
||||
return order && order.payStatus === "PAID" && order.orderStatus !== "CANCELLED";
|
||||
},
|
||||
canNoShowRefund() {
|
||||
return this.canPlatformRefund;
|
||||
},
|
||||
ecouponCardKeyDelivered() {
|
||||
return (this.data || []).some((item) => item.cardKeyDelivered);
|
||||
},
|
||||
ecouponCardKeyRows() {
|
||||
const rows = [];
|
||||
(this.data || []).forEach((item) => {
|
||||
(item.cardKeys || []).forEach((ck) => {
|
||||
rows.push({ ...ck });
|
||||
});
|
||||
});
|
||||
return rows;
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
typeList: [],
|
||||
showPrices: false,
|
||||
printHiddenFlag: false,//隐藏信息
|
||||
loading: false, //加载表格
|
||||
|
||||
addr: "", //地区
|
||||
|
||||
|
||||
orderLogInfo: [], //订单日志数据
|
||||
orderLogModal: false, //弹出调整价格框
|
||||
checkedLogistics: [], //选中的物流公司集合
|
||||
allowOperation: {}, //订单可才做选项
|
||||
sn: "", //订单编号
|
||||
orderInfo: {
|
||||
order: {
|
||||
priceDetailDTO: {},
|
||||
},
|
||||
},
|
||||
appointmentExt: {},
|
||||
modal: false, //弹出调整价格框
|
||||
searchForm: {
|
||||
pageNumber: 1, // 当前页数
|
||||
pageSize: 100, // 页面大小
|
||||
orderSn: "", //订单sn
|
||||
},
|
||||
//调整价格表单
|
||||
modifyPriceForm: {
|
||||
price: 0,
|
||||
},
|
||||
//订单取消表单
|
||||
orderCancelForm: {
|
||||
reason: "",
|
||||
},
|
||||
//弹出订单取消框
|
||||
orderCancelModal: false,
|
||||
//订单发货
|
||||
orderDeliveryForm: {
|
||||
logisticsNo: "", //发货单号
|
||||
logisticsId: "", //物流公司
|
||||
},
|
||||
//验证要调整的订单金额
|
||||
modifyPriceValidate: {
|
||||
reason: [
|
||||
{ required: true, message: "请输入大于0小于99999的合法金额" },
|
||||
{
|
||||
pattern: /^[1-9]\d{0,3}(\.\d{1,2})?$/,
|
||||
message: "请输入大于0小于9999的合法金额",
|
||||
trigger: "change",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
//验证取消订单原因
|
||||
orderCancelValidate: {
|
||||
reason: [{ required: true, message: "取消原因不能为空", trigger: "blur" }],
|
||||
},
|
||||
addressModal: false, //弹出修改收件信息框
|
||||
printModal: false,
|
||||
platformRefundModal: false,
|
||||
noShowRefundModal: false,
|
||||
refundLoading: false,
|
||||
platformRefundForm: {
|
||||
refundType: "UNSERVED",
|
||||
refundAmount: 0,
|
||||
reason: "",
|
||||
},
|
||||
noShowRefundForm: {
|
||||
refundAmount: 0,
|
||||
reason: "",
|
||||
},
|
||||
//收件地址表单
|
||||
addressForm: {
|
||||
consigneeName: "",
|
||||
consigneeMobile: "",
|
||||
consigneeDetail: "",
|
||||
consigneeAddressPath: "",
|
||||
consigneeAddressIdPath: "",
|
||||
},
|
||||
orderDeliverFormValidate: {
|
||||
logisticsNo: [{ required: true, message: "发货单号不能为空", trigger: "change" }],
|
||||
logisticsId: [{ required: true, message: "请选择物流公司", trigger: "blur" }],
|
||||
},
|
||||
addressRule: {
|
||||
consigneeName: [
|
||||
{ required: true, message: "收货人姓名不能为空", trigger: "blur" },
|
||||
],
|
||||
consigneeMobile: [
|
||||
{ required: true, message: "联系方式不能为空", trigger: "blur" },
|
||||
{
|
||||
pattern: RegExp.mobile,
|
||||
trigger: "blur",
|
||||
message: "请输入正确的手机号",
|
||||
},
|
||||
],
|
||||
consigneeDetail: [
|
||||
{ required: true, message: "详细地址不能为空", trigger: "blur" },
|
||||
],
|
||||
},
|
||||
|
||||
data: [], // 表单数据
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
$route (to, from) {
|
||||
this.$router.go(0);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
unitPrice,
|
||||
clientTypeWay,
|
||||
formatCardKeyStatus,
|
||||
copyCardKey(row) {
|
||||
const text = `卡号:${row.cardNo || ""}\n卡密:${row.cardSecret || ""}`;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
ElMessage.success("已复制到剪贴板");
|
||||
});
|
||||
} else {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(ta);
|
||||
ElMessage.success("已复制到剪贴板");
|
||||
}
|
||||
},
|
||||
getPromotionText(row) {
|
||||
let resultText = "";
|
||||
if (row && row.promotionType) {
|
||||
const type = row.promotionType.split(",");
|
||||
if (type.indexOf("PINTUAN") != -1) resultText += "拼团 ";
|
||||
if (type.indexOf("SECKILL") != -1) resultText += "秒杀 ";
|
||||
if (type.indexOf("COUPON") != -1) resultText += "优惠券 ";
|
||||
if (type.indexOf("FULL_DISCOUNT") != -1) resultText += "满减 ";
|
||||
if (type.indexOf("POINTS_GOODS") != -1) resultText += "积分商品 ";
|
||||
}
|
||||
return resultText === "" ? "未参与" : resultText;
|
||||
},
|
||||
gotoHomes () {
|
||||
return false
|
||||
},
|
||||
|
||||
//确认收款
|
||||
confirmPrice () {
|
||||
ElMessageBox.confirm(
|
||||
"您确定要收款吗?线下收款涉及库存变更,需异步进行,等待约一分钟刷新列表查看",
|
||||
"提示",
|
||||
{ type: "warning" }
|
||||
).then(() => {
|
||||
API_Order.orderPay(this.sn).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("收款成功");
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
},
|
||||
getOrderPrice () {
|
||||
if (this.showPrices) {
|
||||
this.showPrices = false
|
||||
} else if (!this.showPrices) {
|
||||
this.showPrices = true
|
||||
}
|
||||
},
|
||||
getContentPrice () {
|
||||
for (let i = 0; i < this.typeList.length; i++) {
|
||||
for (let j = i + 1; j < this.typeList.length; j++) {
|
||||
if (this.typeList[i].promotionId === this.typeList[j].promotionId) {
|
||||
this.typeList[i].discountPrice = this.typeList[i].discountPrice + this.typeList[j].discountPrice
|
||||
this.typeList.splice(j, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.typeList.length >= 3) {
|
||||
this.getContentPrice()
|
||||
}
|
||||
},
|
||||
// 获取订单详情
|
||||
getDataList () {
|
||||
this.loading = true;
|
||||
API_Order.orderDetail(this.sn).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.orderInfo = res.result;
|
||||
this.allowOperation = res.result.allowOperationVO;
|
||||
this.data = res.result.orderItems;
|
||||
this.typeList = JSON.parse(JSON.stringify(res.result.order.priceDetailDTO.discountPriceDetail));
|
||||
this.getContentPrice()
|
||||
this.getOrderPrice()
|
||||
if (this.isAppointmentOrder) {
|
||||
this.loadAppointmentExt();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
loadAppointmentExt() {
|
||||
getAppointmentOrderDetail(this.sn)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.appointmentExt = res.result || {};
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.appointmentExt = {};
|
||||
});
|
||||
},
|
||||
parseFormAnswerGroups(formAnswerJson) {
|
||||
if (!formAnswerJson) return [];
|
||||
let data = formAnswerJson;
|
||||
if (typeof data === "string") {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
const fields = Array.isArray(data?.fields) ? data.fields : [];
|
||||
const answers = Array.isArray(data?.answers) ? data.answers : [];
|
||||
return answers
|
||||
.map((group) => {
|
||||
const map = group && typeof group === "object" && !Array.isArray(group) ? group : {};
|
||||
const source = fields.length
|
||||
? fields
|
||||
: Object.keys(map).map((key) => ({ fieldKey: key, fieldLabel: key }));
|
||||
return source
|
||||
.map((field) => ({
|
||||
fieldKey: field.fieldKey,
|
||||
fieldLabel: field.fieldLabel || field.fieldKey,
|
||||
value: map[field.fieldKey] ?? "",
|
||||
}))
|
||||
.filter((row) => row.value !== "" && row.value != null);
|
||||
})
|
||||
.filter((rows) => rows.length);
|
||||
},
|
||||
modifyPrice () {
|
||||
//默认要修改的金额为订单总金额
|
||||
this.modifyPriceForm.price = this.orderInfo.order.flowPrice;
|
||||
this.modal = true;
|
||||
},
|
||||
//修改订单金额提交
|
||||
modifyPriceSubmit () {
|
||||
this.$refs.modifyPriceForm.validate((valid) => {
|
||||
if (valid) {
|
||||
API_Order.updateOrderPrice(this.sn, this.modifyPriceForm).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("修改订单金额成功");
|
||||
this.modal = false;
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
// 选中的地址
|
||||
selectedRegion (val) {
|
||||
if(val.type === 'select'){
|
||||
const paths = val.data.map(item => item.name).join(',')
|
||||
const ids = val.data.map(item => item.id).join(',')
|
||||
this.addr = paths;
|
||||
this.regionId = ids;
|
||||
}
|
||||
else{
|
||||
this.addr = val.data.addr;
|
||||
this.regionId = val.data.addrId;
|
||||
}
|
||||
|
||||
},
|
||||
//订单取消
|
||||
orderCancel () {
|
||||
this.orderCancelModal = true;
|
||||
},
|
||||
//订单取消提交
|
||||
orderCancelSubmit () {
|
||||
this.$refs.orderCancelForm.validate((valid) => {
|
||||
if (valid) {
|
||||
API_Order.orderCancel(this.sn, this.orderCancelForm).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("取消成功");
|
||||
this.getDataList();
|
||||
}
|
||||
this.orderCancelModal = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
openPlatformRefund() {
|
||||
this.platformRefundForm = {
|
||||
refundType: "UNSERVED",
|
||||
refundAmount: this.orderInfo?.order?.flowPrice || 0,
|
||||
reason: "",
|
||||
};
|
||||
this.platformRefundModal = true;
|
||||
},
|
||||
submitPlatformRefund() {
|
||||
if (!this.platformRefundForm.reason) {
|
||||
ElMessage.warning("请填写退款原因");
|
||||
return;
|
||||
}
|
||||
this.refundLoading = true;
|
||||
platformRefund(this.sn, this.platformRefundForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("退款成功");
|
||||
this.platformRefundModal = false;
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.refundLoading = false;
|
||||
});
|
||||
},
|
||||
openNoShowRefund() {
|
||||
this.noShowRefundForm = {
|
||||
refundAmount: this.orderInfo?.order?.flowPrice || 0,
|
||||
reason: "",
|
||||
};
|
||||
this.noShowRefundModal = true;
|
||||
},
|
||||
submitNoShowRefund() {
|
||||
if (!this.noShowRefundForm.reason) {
|
||||
ElMessage.warning("请填写退款原因");
|
||||
return;
|
||||
}
|
||||
this.refundLoading = true;
|
||||
noShowRefund(this.sn, this.noShowRefundForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("退款成功");
|
||||
this.noShowRefundModal = false;
|
||||
this.getDataList();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.refundLoading = false;
|
||||
});
|
||||
},
|
||||
//订单日志
|
||||
orderLog () {
|
||||
this.orderLogModal = true;
|
||||
},
|
||||
//订单日志取消
|
||||
handelCancel () {
|
||||
this.orderLogModal = false;
|
||||
},
|
||||
//打印发货单
|
||||
printOrder () {
|
||||
this.printModal = true;
|
||||
},
|
||||
printHiddenInfo () {
|
||||
this.printHiddenFlag = !this.printHiddenFlag;
|
||||
},
|
||||
printCancel () {
|
||||
// this.printHiddenFlag = false;
|
||||
},
|
||||
handlePrintInvoice () {
|
||||
printElement("printInfo", "发货单");
|
||||
},
|
||||
//弹出修改收货地址框
|
||||
editAddress () {
|
||||
this.addressModal = true;
|
||||
this.addr = this.orderInfo.order.consigneeAddressPath;
|
||||
this.regionId = this.orderInfo.order.consigneeAddressIdPath;
|
||||
this.addressForm.consigneeName = this.orderInfo.order.consigneeName;
|
||||
this.addressForm.consigneeMobile = this.orderInfo.order.consigneeMobile;
|
||||
this.addressForm.consigneeDetail = this.orderInfo.order.consigneeDetail;
|
||||
this.addressForm.consigneeAddressPath = this.orderInfo.order.consigneeAddressPath;
|
||||
this.addressForm.consigneeAddressIdPath = this.orderInfo.order.consigneeAddressIdPath;
|
||||
},
|
||||
//修改收货地址
|
||||
editAddressSubmit () {
|
||||
if (this.regionId == "") {
|
||||
ElMessage.error("请选择地址");
|
||||
return;
|
||||
}
|
||||
this.addressForm.consigneeAddressPath = this.addr;
|
||||
this.addressForm.consigneeAddressIdPath = this.regionId;
|
||||
this.$refs.addressForm.validate((valid) => {
|
||||
if (valid) {
|
||||
API_Order.editOrderConsignee(this.sn, this.addressForm).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("收货地址修改成功");
|
||||
this.addressModal = false;
|
||||
this.getDataList();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
this.sn = this.$route.query.sn;
|
||||
this.getDataList();
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
//验证取消订单原因
|
||||
const orderCancelValidate = {
|
||||
reason: [{ required: true, message: "取消原因不能为空", trigger: "blur" }],
|
||||
};
|
||||
const addressModal = ref(false); //弹出修改收件信息框
|
||||
const printModal = ref(false);
|
||||
const platformRefundModal = ref(false);
|
||||
const noShowRefundModal = ref(false);
|
||||
const refundLoading = ref(false);
|
||||
const platformRefundForm = ref({
|
||||
refundType: "UNSERVED",
|
||||
refundAmount: 0,
|
||||
reason: "",
|
||||
});
|
||||
const noShowRefundForm = ref({
|
||||
refundAmount: 0,
|
||||
reason: "",
|
||||
});
|
||||
//收件地址表单
|
||||
const addressForm = reactive({
|
||||
consigneeName: "",
|
||||
consigneeMobile: "",
|
||||
consigneeDetail: "",
|
||||
consigneeAddressPath: "",
|
||||
consigneeAddressIdPath: "",
|
||||
});
|
||||
const orderDeliverFormValidate = {
|
||||
logisticsNo: [{ required: true, message: "发货单号不能为空", trigger: "change" }],
|
||||
logisticsId: [{ required: true, message: "请选择物流公司", trigger: "blur" }],
|
||||
};
|
||||
const addressRule = {
|
||||
consigneeName: [
|
||||
{ required: true, message: "收货人姓名不能为空", trigger: "blur" },
|
||||
],
|
||||
consigneeMobile: [
|
||||
{ required: true, message: "联系方式不能为空", trigger: "blur" },
|
||||
{
|
||||
pattern: RegExp.mobile,
|
||||
trigger: "blur",
|
||||
message: "请输入正确的手机号",
|
||||
},
|
||||
],
|
||||
consigneeDetail: [
|
||||
{ required: true, message: "详细地址不能为空", trigger: "blur" },
|
||||
],
|
||||
};
|
||||
|
||||
const data = ref([]); // 表单数据
|
||||
|
||||
/** 电子卡券订单:无物流区,卡密来自 orderItems[].cardKeys */
|
||||
const isECouponOrder = computed(() => {
|
||||
const t = orderInfo.value?.order?.orderType;
|
||||
return t === "E_COUPON" || route.query.orderType === "E_COUPON";
|
||||
});
|
||||
const isVirtualOrder = computed(() => {
|
||||
const t = orderInfo.value?.order?.orderType;
|
||||
return t === "VIRTUAL" || route.query.orderType === "VIRTUAL";
|
||||
});
|
||||
const isNonPhysicalOrder = computed(() => {
|
||||
return isVirtualOrder.value || isECouponOrder.value || isAppointmentOrder.value;
|
||||
});
|
||||
const isAppointmentOrder = computed(() => {
|
||||
const t = orderInfo.value?.order?.orderType;
|
||||
return t === "APPOINTMENT" || route.query.orderType === "APPOINTMENT";
|
||||
});
|
||||
const appointmentSlotText = computed(() => {
|
||||
const v = appointmentExt.value || {};
|
||||
if (!v.slotDate) return "";
|
||||
const date = String(v.slotDate).slice(0, 10);
|
||||
const slot = v.slotStartTime && v.slotEndTime ? `${v.slotStartTime}-${v.slotEndTime}` : "";
|
||||
return [date, slot].filter(Boolean).join(" ");
|
||||
});
|
||||
const appointmentFormGroups = computed(() => {
|
||||
return parseFormAnswerGroups(appointmentExt.value?.formAnswerJson);
|
||||
});
|
||||
const canPlatformRefund = computed(() => {
|
||||
const order = orderInfo.value?.order;
|
||||
return order && order.payStatus === "PAID" && order.orderStatus !== "CANCELLED";
|
||||
});
|
||||
const canNoShowRefund = computed(() => {
|
||||
return canPlatformRefund.value;
|
||||
});
|
||||
const ecouponCardKeyDelivered = computed(() => {
|
||||
return (data.value || []).some((item) => item.cardKeyDelivered);
|
||||
});
|
||||
const ecouponCardKeyRows = computed(() => {
|
||||
const rows = [];
|
||||
(data.value || []).forEach((item) => {
|
||||
(item.cardKeys || []).forEach((ck) => {
|
||||
rows.push({ ...ck });
|
||||
});
|
||||
});
|
||||
return rows;
|
||||
});
|
||||
|
||||
watch(
|
||||
() => route.query,
|
||||
(to, from) => {
|
||||
router.go(0);
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
sn.value = route.query.sn;
|
||||
getDataList();
|
||||
});
|
||||
|
||||
function copyCardKey(row) {
|
||||
const text = `卡号:${row.cardNo || ""}\n卡密:${row.cardSecret || ""}`;
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
ElMessage.success("已复制到剪贴板");
|
||||
});
|
||||
} else {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.value = text;
|
||||
document.body.appendChild(ta);
|
||||
ta.select();
|
||||
document.execCommand("copy");
|
||||
document.body.removeChild(ta);
|
||||
ElMessage.success("已复制到剪贴板");
|
||||
}
|
||||
}
|
||||
function getPromotionText(row) {
|
||||
let resultText = "";
|
||||
if (row && row.promotionType) {
|
||||
const type = row.promotionType.split(",");
|
||||
if (type.indexOf("PINTUAN") != -1) resultText += "拼团 ";
|
||||
if (type.indexOf("SECKILL") != -1) resultText += "秒杀 ";
|
||||
if (type.indexOf("COUPON") != -1) resultText += "优惠券 ";
|
||||
if (type.indexOf("FULL_DISCOUNT") != -1) resultText += "满减 ";
|
||||
if (type.indexOf("POINTS_GOODS") != -1) resultText += "积分商品 ";
|
||||
}
|
||||
return resultText === "" ? "未参与" : resultText;
|
||||
}
|
||||
function gotoHomes () {
|
||||
return false
|
||||
}
|
||||
|
||||
//确认收款
|
||||
function confirmPrice () {
|
||||
ElMessageBox.confirm(
|
||||
"您确定要收款吗?线下收款涉及库存变更,需异步进行,等待约一分钟刷新列表查看",
|
||||
"提示",
|
||||
{ type: "warning" }
|
||||
).then(() => {
|
||||
API_Order.orderPay(sn.value).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("收款成功");
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}).catch(() => {});
|
||||
}
|
||||
function getOrderPrice () {
|
||||
if (showPrices.value) {
|
||||
showPrices.value = false
|
||||
} else if (!showPrices.value) {
|
||||
showPrices.value = true
|
||||
}
|
||||
}
|
||||
function getContentPrice () {
|
||||
for (let i = 0; i < typeList.value.length; i++) {
|
||||
for (let j = i + 1; j < typeList.value.length; j++) {
|
||||
if (typeList.value[i].promotionId === typeList.value[j].promotionId) {
|
||||
typeList.value[i].discountPrice = typeList.value[i].discountPrice + typeList.value[j].discountPrice
|
||||
typeList.value.splice(j, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeList.value.length >= 3) {
|
||||
getContentPrice()
|
||||
}
|
||||
}
|
||||
// 获取订单详情
|
||||
function getDataList () {
|
||||
loading.value = true;
|
||||
API_Order.orderDetail(sn.value).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
orderInfo.value = res.result;
|
||||
allowOperation.value = res.result.allowOperationVO;
|
||||
data.value = res.result.orderItems;
|
||||
typeList.value = JSON.parse(JSON.stringify(res.result.order.priceDetailDTO.discountPriceDetail));
|
||||
getContentPrice()
|
||||
getOrderPrice()
|
||||
if (isAppointmentOrder.value) {
|
||||
loadAppointmentExt();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function loadAppointmentExt() {
|
||||
getAppointmentOrderDetail(sn.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
appointmentExt.value = res.result || {};
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
appointmentExt.value = {};
|
||||
});
|
||||
}
|
||||
function parseFormAnswerGroups(formAnswerJson) {
|
||||
if (!formAnswerJson) return [];
|
||||
let data = formAnswerJson;
|
||||
if (typeof data === "string") {
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
const fields = Array.isArray(data?.fields) ? data.fields : [];
|
||||
const answers = Array.isArray(data?.answers) ? data.answers : [];
|
||||
return answers
|
||||
.map((group) => {
|
||||
const map = group && typeof group === "object" && !Array.isArray(group) ? group : {};
|
||||
const source = fields.length
|
||||
? fields
|
||||
: Object.keys(map).map((key) => ({ fieldKey: key, fieldLabel: key }));
|
||||
return source
|
||||
.map((field) => ({
|
||||
fieldKey: field.fieldKey,
|
||||
fieldLabel: field.fieldLabel || field.fieldKey,
|
||||
value: map[field.fieldKey] ?? "",
|
||||
}))
|
||||
.filter((row) => row.value !== "" && row.value != null);
|
||||
})
|
||||
.filter((rows) => rows.length);
|
||||
}
|
||||
function modifyPrice () {
|
||||
//默认要修改的金额为订单总金额
|
||||
modifyPriceForm.price = orderInfo.value.order.flowPrice;
|
||||
modal.value = true;
|
||||
}
|
||||
//修改订单金额提交
|
||||
function modifyPriceSubmit () {
|
||||
modifyPriceFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
API_Order.updateOrderPrice(sn.value, modifyPriceForm).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("修改订单金额成功");
|
||||
modal.value = false;
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// 选中的地址
|
||||
function selectedRegion (val) {
|
||||
if(val.type === 'select'){
|
||||
const paths = val.data.map(item => item.name).join(',')
|
||||
const ids = val.data.map(item => item.id).join(',')
|
||||
addr.value = paths;
|
||||
regionId = ids;
|
||||
}
|
||||
else{
|
||||
addr.value = val.data.addr;
|
||||
regionId = val.data.addrId;
|
||||
}
|
||||
|
||||
}
|
||||
//订单取消
|
||||
function orderCancel () {
|
||||
orderCancelModal.value = true;
|
||||
}
|
||||
//订单取消提交
|
||||
function orderCancelSubmit () {
|
||||
orderCancelFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
API_Order.orderCancel(sn.value, orderCancelForm).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("取消成功");
|
||||
getDataList();
|
||||
}
|
||||
orderCancelModal.value = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function openPlatformRefund() {
|
||||
platformRefundForm.value = {
|
||||
refundType: "UNSERVED",
|
||||
refundAmount: orderInfo.value?.order?.flowPrice || 0,
|
||||
reason: "",
|
||||
};
|
||||
platformRefundModal.value = true;
|
||||
}
|
||||
function submitPlatformRefund() {
|
||||
if (!platformRefundForm.value.reason) {
|
||||
ElMessage.warning("请填写退款原因");
|
||||
return;
|
||||
}
|
||||
refundLoading.value = true;
|
||||
platformRefund(sn.value, platformRefundForm.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("退款成功");
|
||||
platformRefundModal.value = false;
|
||||
getDataList();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
refundLoading.value = false;
|
||||
});
|
||||
}
|
||||
function openNoShowRefund() {
|
||||
noShowRefundForm.value = {
|
||||
refundAmount: orderInfo.value?.order?.flowPrice || 0,
|
||||
reason: "",
|
||||
};
|
||||
noShowRefundModal.value = true;
|
||||
}
|
||||
function submitNoShowRefund() {
|
||||
if (!noShowRefundForm.value.reason) {
|
||||
ElMessage.warning("请填写退款原因");
|
||||
return;
|
||||
}
|
||||
refundLoading.value = true;
|
||||
noShowRefund(sn.value, noShowRefundForm.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("退款成功");
|
||||
noShowRefundModal.value = false;
|
||||
getDataList();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
refundLoading.value = false;
|
||||
});
|
||||
}
|
||||
//订单日志
|
||||
function orderLog () {
|
||||
orderLogModal.value = true;
|
||||
}
|
||||
//订单日志取消
|
||||
function handelCancel () {
|
||||
orderLogModal.value = false;
|
||||
}
|
||||
//打印发货单
|
||||
function printOrder () {
|
||||
printModal.value = true;
|
||||
}
|
||||
function printHiddenInfo () {
|
||||
printHiddenFlag.value = !printHiddenFlag.value;
|
||||
}
|
||||
function printCancel () {
|
||||
// printHiddenFlag.value = false;
|
||||
}
|
||||
function handlePrintInvoice () {
|
||||
printElement("printInfo", "发货单");
|
||||
}
|
||||
//弹出修改收货地址框
|
||||
function editAddress () {
|
||||
addressModal.value = true;
|
||||
addr.value = orderInfo.value.order.consigneeAddressPath;
|
||||
regionId = orderInfo.value.order.consigneeAddressIdPath;
|
||||
addressForm.consigneeName = orderInfo.value.order.consigneeName;
|
||||
addressForm.consigneeMobile = orderInfo.value.order.consigneeMobile;
|
||||
addressForm.consigneeDetail = orderInfo.value.order.consigneeDetail;
|
||||
addressForm.consigneeAddressPath = orderInfo.value.order.consigneeAddressPath;
|
||||
addressForm.consigneeAddressIdPath = orderInfo.value.order.consigneeAddressIdPath;
|
||||
}
|
||||
//修改收货地址
|
||||
function editAddressSubmit () {
|
||||
if (regionId == "") {
|
||||
ElMessage.error("请选择地址");
|
||||
return;
|
||||
}
|
||||
addressForm.consigneeAddressPath = addr.value;
|
||||
addressForm.consigneeAddressIdPath = regionId;
|
||||
addressFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
API_Order.editOrderConsignee(sn.value, addressForm).then((res) => {
|
||||
if (res.success) {
|
||||
ElMessage.success("收货地址修改成功");
|
||||
addressModal.value = false;
|
||||
getDataList();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.lineH30 {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="search">
|
||||
<el-card>
|
||||
<el-form
|
||||
ref="searchForm"
|
||||
ref="searchFormRef"
|
||||
:model="searchForm"
|
||||
label-width="70px"
|
||||
class="search-form order-search-form"
|
||||
@@ -74,7 +74,7 @@
|
||||
<el-button type="warning" class="export" :loading="exporting" @click="exportOrder">导出订单</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="data" ref="table" class="mt_10" style="width: 100%">
|
||||
<el-table v-loading="loading" :data="data" ref="tableRef" class="mt_10" style="width: 100%">
|
||||
<el-table-column prop="sn" label="订单号" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="商品" min-width="280">
|
||||
<template #default="{ row }">
|
||||
@@ -149,7 +149,8 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import * as API_Order from "@/api/order";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { unitPrice, customRouterPush } from "@/utils/filters";
|
||||
@@ -229,264 +230,260 @@ function readSavedQuery() {
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "orderList",
|
||||
data() {
|
||||
return {
|
||||
loading: true,
|
||||
exporting: false,
|
||||
pageSizes: PAGE_SIZES,
|
||||
promotionOptions: PROMOTION_OPTIONS,
|
||||
orderTypeOptions: ORDER_TYPE_OPTIONS,
|
||||
paymentOptions: PAYMENT_OPTIONS,
|
||||
searchForm: emptySearchForm(),
|
||||
selectDate: null,
|
||||
data: [],
|
||||
total: 0,
|
||||
orderNumData: {},
|
||||
currentStatus: "ALL",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
orderStatusWithCount() {
|
||||
const count = (value) => (value ? `(${value})` : "");
|
||||
return [
|
||||
{ title: "全部", value: "ALL" },
|
||||
{ title: `未付款${count(this.orderNumData.waitPayNum)}`, value: "UNPAID" },
|
||||
{ title: `已付款${count(this.orderNumData.waitDeliveryNum)}`, value: "PAID" },
|
||||
{ title: `待发货${count(this.orderNumData.waitShipNum)}`, value: "UNDELIVERED" },
|
||||
{ title: `部分发货${count(this.orderNumData.partsDeliveredNumNum)}`, value: "PARTS_DELIVERED" },
|
||||
{ title: `待收货${count(this.orderNumData.deliveredNum)}`, value: "DELIVERED" },
|
||||
{ title: `待核验${count(this.orderNumData.waitCheckNum)}`, value: "TAKE" },
|
||||
{ title: `待自提${count(this.orderNumData.waitSelfPickNum)}`, value: "STAY_PICKED_UP" },
|
||||
{ title: `已完成${count(this.orderNumData.finishNum)}`, value: "COMPLETED" },
|
||||
{ title: `已关闭${count(this.orderNumData.closeNum)}`, value: "CANCELLED" },
|
||||
];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
unitPrice,
|
||||
onStatusTabClick(tab) {
|
||||
this.orderStatusClick(tab.paneName);
|
||||
},
|
||||
orderGoods(row) {
|
||||
const items = row && row.orderItems;
|
||||
return items && items.length ? items : [];
|
||||
},
|
||||
clientTypeText(v) {
|
||||
const map = { H5: "移动端", PC: "PC端", WECHAT_MP: "小程序端", APP: "移动应用端" };
|
||||
return map[v] || v || "—";
|
||||
},
|
||||
orderTypeText(v) {
|
||||
const found = ORDER_TYPE_OPTIONS.find((item) => item.value === v);
|
||||
return found ? found.label : v || "—";
|
||||
},
|
||||
orderPromotionText(v) {
|
||||
const found = PROMOTION_OPTIONS.find((item) => item.value === v);
|
||||
return found ? found.label : v || "—";
|
||||
},
|
||||
orderPromotionTagType(v) {
|
||||
const map = {
|
||||
NORMAL: "primary",
|
||||
PINTUAN: "danger",
|
||||
GIFT: "success",
|
||||
POINTS: "info",
|
||||
KANJIA: "warning",
|
||||
};
|
||||
return map[v] || "info";
|
||||
},
|
||||
deliveryMethodText(v) {
|
||||
const map = {
|
||||
SELF_PICK_UP: "自提",
|
||||
LOCAL_TOWN_DELIVERY: "同城配送",
|
||||
LOGISTICS: "物流",
|
||||
VIRTUAL: "虚拟发货",
|
||||
};
|
||||
return map[v] || v || "—";
|
||||
},
|
||||
paymentMethodText(v) {
|
||||
const map = {
|
||||
NOT_ACTUALLY_PAID: "—",
|
||||
WECHAT: "微信支付",
|
||||
ALIPAY: "支付宝",
|
||||
WALLET: "余额支付",
|
||||
BANK_TRANSFER: "线下转账",
|
||||
};
|
||||
return map[v] || v || "—";
|
||||
},
|
||||
orderStatusText(v) {
|
||||
const map = {
|
||||
UNPAID: "未付款",
|
||||
PAID: "已付款",
|
||||
UNDELIVERED: "待发货",
|
||||
STAY_PICKED_UP: "待自提",
|
||||
PARTS_DELIVERED: "部分发货",
|
||||
DELIVERED: "已发货",
|
||||
COMPLETED: "已完成",
|
||||
TAKE: "待核验",
|
||||
CANCELLED: "已关闭",
|
||||
};
|
||||
return map[v] || v || "—";
|
||||
},
|
||||
orderStatusTagType(v) {
|
||||
const map = {
|
||||
UNPAID: "danger",
|
||||
PAID: "primary",
|
||||
UNDELIVERED: "info",
|
||||
STAY_PICKED_UP: "info",
|
||||
PARTS_DELIVERED: "warning",
|
||||
DELIVERED: "warning",
|
||||
COMPLETED: "success",
|
||||
TAKE: "warning",
|
||||
CANCELLED: "danger",
|
||||
};
|
||||
return map[v] || "info";
|
||||
},
|
||||
rememberQuery() {
|
||||
sessionStorage.setItem(
|
||||
QUERY_KEY,
|
||||
JSON.stringify({
|
||||
searchForm: this.searchForm,
|
||||
selectDate: this.selectDate,
|
||||
currentStatus: this.currentStatus,
|
||||
})
|
||||
defineOptions({ name: "orderList" });
|
||||
|
||||
const searchFormRef = ref(null);
|
||||
const tableRef = ref(null);
|
||||
|
||||
const loading = ref(true);
|
||||
const exporting = ref(false);
|
||||
const pageSizes = ref(PAGE_SIZES);
|
||||
const promotionOptions = ref(PROMOTION_OPTIONS);
|
||||
const orderTypeOptions = ref(ORDER_TYPE_OPTIONS);
|
||||
const paymentOptions = ref(PAYMENT_OPTIONS);
|
||||
const searchForm = ref(emptySearchForm());
|
||||
const selectDate = ref(null);
|
||||
const data = ref([]);
|
||||
const total = ref(0);
|
||||
const orderNumData = ref({});
|
||||
const currentStatus = ref("ALL");
|
||||
|
||||
const orderStatusWithCount = computed(() => {
|
||||
const count = (value) => (value ? `(${value})` : "");
|
||||
return [
|
||||
{ title: "全部", value: "ALL" },
|
||||
{ title: `未付款${count(orderNumData.value.waitPayNum)}`, value: "UNPAID" },
|
||||
{ title: `已付款${count(orderNumData.value.waitDeliveryNum)}`, value: "PAID" },
|
||||
{ title: `待发货${count(orderNumData.value.waitShipNum)}`, value: "UNDELIVERED" },
|
||||
{ title: `部分发货${count(orderNumData.value.partsDeliveredNumNum)}`, value: "PARTS_DELIVERED" },
|
||||
{ title: `待收货${count(orderNumData.value.deliveredNum)}`, value: "DELIVERED" },
|
||||
{ title: `待核验${count(orderNumData.value.waitCheckNum)}`, value: "TAKE" },
|
||||
{ title: `待自提${count(orderNumData.value.waitSelfPickNum)}`, value: "STAY_PICKED_UP" },
|
||||
{ title: `已完成${count(orderNumData.value.finishNum)}`, value: "COMPLETED" },
|
||||
{ title: `已关闭${count(orderNumData.value.closeNum)}`, value: "CANCELLED" },
|
||||
];
|
||||
});
|
||||
|
||||
function onStatusTabClick(tab) {
|
||||
orderStatusClick(tab.paneName);
|
||||
}
|
||||
function orderGoods(row) {
|
||||
const items = row && row.orderItems;
|
||||
return items && items.length ? items : [];
|
||||
}
|
||||
function clientTypeText(v) {
|
||||
const map = { H5: "移动端", PC: "PC端", WECHAT_MP: "小程序端", APP: "移动应用端" };
|
||||
return map[v] || v || "—";
|
||||
}
|
||||
function orderTypeText(v) {
|
||||
const found = ORDER_TYPE_OPTIONS.find((item) => item.value === v);
|
||||
return found ? found.label : v || "—";
|
||||
}
|
||||
function orderPromotionText(v) {
|
||||
const found = PROMOTION_OPTIONS.find((item) => item.value === v);
|
||||
return found ? found.label : v || "—";
|
||||
}
|
||||
function orderPromotionTagType(v) {
|
||||
const map = {
|
||||
NORMAL: "primary",
|
||||
PINTUAN: "danger",
|
||||
GIFT: "success",
|
||||
POINTS: "info",
|
||||
KANJIA: "warning",
|
||||
};
|
||||
return map[v] || "info";
|
||||
}
|
||||
function deliveryMethodText(v) {
|
||||
const map = {
|
||||
SELF_PICK_UP: "自提",
|
||||
LOCAL_TOWN_DELIVERY: "同城配送",
|
||||
LOGISTICS: "物流",
|
||||
VIRTUAL: "虚拟发货",
|
||||
};
|
||||
return map[v] || v || "—";
|
||||
}
|
||||
function paymentMethodText(v) {
|
||||
const map = {
|
||||
NOT_ACTUALLY_PAID: "—",
|
||||
WECHAT: "微信支付",
|
||||
ALIPAY: "支付宝",
|
||||
WALLET: "余额支付",
|
||||
BANK_TRANSFER: "线下转账",
|
||||
};
|
||||
return map[v] || v || "—";
|
||||
}
|
||||
function orderStatusText(v) {
|
||||
const map = {
|
||||
UNPAID: "未付款",
|
||||
PAID: "已付款",
|
||||
UNDELIVERED: "待发货",
|
||||
STAY_PICKED_UP: "待自提",
|
||||
PARTS_DELIVERED: "部分发货",
|
||||
DELIVERED: "已发货",
|
||||
COMPLETED: "已完成",
|
||||
TAKE: "待核验",
|
||||
CANCELLED: "已关闭",
|
||||
};
|
||||
return map[v] || v || "—";
|
||||
}
|
||||
function orderStatusTagType(v) {
|
||||
const map = {
|
||||
UNPAID: "danger",
|
||||
PAID: "primary",
|
||||
UNDELIVERED: "info",
|
||||
STAY_PICKED_UP: "info",
|
||||
PARTS_DELIVERED: "warning",
|
||||
DELIVERED: "warning",
|
||||
COMPLETED: "success",
|
||||
TAKE: "warning",
|
||||
CANCELLED: "danger",
|
||||
};
|
||||
return map[v] || "info";
|
||||
}
|
||||
function rememberQuery() {
|
||||
sessionStorage.setItem(
|
||||
QUERY_KEY,
|
||||
JSON.stringify({
|
||||
searchForm: searchForm.value,
|
||||
selectDate: selectDate.value,
|
||||
currentStatus: currentStatus.value,
|
||||
})
|
||||
);
|
||||
}
|
||||
function restoreQuery() {
|
||||
const saved = readSavedQuery();
|
||||
if (!saved) return;
|
||||
searchForm.value = saved.searchForm;
|
||||
selectDate.value = saved.selectDate;
|
||||
currentStatus.value = saved.currentStatus;
|
||||
}
|
||||
function init() {
|
||||
getDataList();
|
||||
getOrderNumData();
|
||||
}
|
||||
function changePage(v) {
|
||||
searchForm.value.pageNumber = v;
|
||||
getDataList();
|
||||
}
|
||||
function changePageSize(v) {
|
||||
searchForm.value.pageNumber = 1;
|
||||
searchForm.value.pageSize = v;
|
||||
getDataList();
|
||||
}
|
||||
function handleSearch() {
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
getOrderNumData();
|
||||
}
|
||||
function handleReset() {
|
||||
const pageSize = searchForm.value.pageSize;
|
||||
searchForm.value = emptySearchForm(pageSize);
|
||||
selectDate.value = null;
|
||||
currentStatus.value = "ALL";
|
||||
sessionStorage.removeItem(QUERY_KEY);
|
||||
init();
|
||||
}
|
||||
function selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
searchForm.value.startDate = v[0];
|
||||
searchForm.value.endDate = v[1];
|
||||
} else {
|
||||
searchForm.value.startDate = "";
|
||||
searchForm.value.endDate = "";
|
||||
}
|
||||
}
|
||||
function countParams() {
|
||||
const params = { ...searchForm.value };
|
||||
delete params.orderStatus;
|
||||
delete params.pageNumber;
|
||||
delete params.pageSize;
|
||||
delete params.sort;
|
||||
delete params.order;
|
||||
return params;
|
||||
}
|
||||
function getDataList() {
|
||||
loading.value = true;
|
||||
rememberQuery();
|
||||
API_Order.getOrderList(searchForm.value)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
data.value = res.result.records;
|
||||
total.value = res.result.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
}
|
||||
function detail(v) {
|
||||
customRouterPush({
|
||||
name: "order-detail",
|
||||
query: { sn: v.sn },
|
||||
});
|
||||
}
|
||||
async function exportOrder() {
|
||||
if (!searchForm.value.startDate || !searchForm.value.endDate) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
"未选择下单时间,将按当前筛选条件导出全部匹配订单。",
|
||||
"导出订单",
|
||||
{ type: "warning", confirmButtonText: "继续导出", cancelButtonText: "取消" }
|
||||
);
|
||||
},
|
||||
restoreQuery() {
|
||||
const saved = readSavedQuery();
|
||||
if (!saved) return;
|
||||
this.searchForm = saved.searchForm;
|
||||
this.selectDate = saved.selectDate;
|
||||
this.currentStatus = saved.currentStatus;
|
||||
},
|
||||
init() {
|
||||
this.getDataList();
|
||||
this.getOrderNumData();
|
||||
},
|
||||
changePage(v) {
|
||||
this.searchForm.pageNumber = v;
|
||||
this.getDataList();
|
||||
},
|
||||
changePageSize(v) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = v;
|
||||
this.getDataList();
|
||||
},
|
||||
handleSearch() {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
this.getOrderNumData();
|
||||
},
|
||||
handleReset() {
|
||||
const pageSize = this.searchForm.pageSize;
|
||||
this.searchForm = emptySearchForm(pageSize);
|
||||
this.selectDate = null;
|
||||
this.currentStatus = "ALL";
|
||||
sessionStorage.removeItem(QUERY_KEY);
|
||||
this.init();
|
||||
},
|
||||
selectDateRange(v) {
|
||||
if (v && v.length === 2) {
|
||||
this.searchForm.startDate = v[0];
|
||||
this.searchForm.endDate = v[1];
|
||||
} else {
|
||||
this.searchForm.startDate = "";
|
||||
this.searchForm.endDate = "";
|
||||
}
|
||||
},
|
||||
countParams() {
|
||||
const params = { ...this.searchForm };
|
||||
delete params.orderStatus;
|
||||
delete params.pageNumber;
|
||||
delete params.pageSize;
|
||||
delete params.sort;
|
||||
delete params.order;
|
||||
return params;
|
||||
},
|
||||
getDataList() {
|
||||
this.loading = true;
|
||||
this.rememberQuery();
|
||||
API_Order.getOrderList(this.searchForm)
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.data = res.result.records;
|
||||
this.total = res.result.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
detail(v) {
|
||||
customRouterPush({
|
||||
name: "order-detail",
|
||||
query: { sn: v.sn },
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
exporting.value = true;
|
||||
API_Order.exportOrder(searchForm.value)
|
||||
.then((res) => {
|
||||
const blob = new Blob([res], {
|
||||
type: "application/vnd.ms-excel;charset=utf-8",
|
||||
});
|
||||
},
|
||||
async exportOrder() {
|
||||
if (!this.searchForm.startDate || !this.searchForm.endDate) {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
"未选择下单时间,将按当前筛选条件导出全部匹配订单。",
|
||||
"导出订单",
|
||||
{ type: "warning", confirmButtonText: "继续导出", cancelButtonText: "取消" }
|
||||
);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
const link = document.createElement("a");
|
||||
link.download = "订单列表.xlsx";
|
||||
link.style.display = "none";
|
||||
link.href = URL.createObjectURL(blob);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
URL.revokeObjectURL(link.href);
|
||||
document.body.removeChild(link);
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("导出失败");
|
||||
})
|
||||
.finally(() => {
|
||||
exporting.value = false;
|
||||
});
|
||||
}
|
||||
function orderStatusClick(name) {
|
||||
if (name === "ALL" || name === "" || name === undefined) {
|
||||
searchForm.value.orderStatus = "";
|
||||
currentStatus.value = "ALL";
|
||||
} else {
|
||||
searchForm.value.orderStatus = name;
|
||||
currentStatus.value = name;
|
||||
}
|
||||
searchForm.value.pageNumber = 1;
|
||||
getDataList();
|
||||
}
|
||||
function getOrderNumData() {
|
||||
API_Order.getOrderNum(countParams())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
orderNumData.value = res.result;
|
||||
}
|
||||
this.exporting = true;
|
||||
API_Order.exportOrder(this.searchForm)
|
||||
.then((res) => {
|
||||
const blob = new Blob([res], {
|
||||
type: "application/vnd.ms-excel;charset=utf-8",
|
||||
});
|
||||
const link = document.createElement("a");
|
||||
link.download = "订单列表.xlsx";
|
||||
link.style.display = "none";
|
||||
link.href = URL.createObjectURL(blob);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
URL.revokeObjectURL(link.href);
|
||||
document.body.removeChild(link);
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.error("导出失败");
|
||||
})
|
||||
.finally(() => {
|
||||
this.exporting = false;
|
||||
});
|
||||
},
|
||||
orderStatusClick(name) {
|
||||
if (name === "ALL" || name === "" || name === undefined) {
|
||||
this.searchForm.orderStatus = "";
|
||||
this.currentStatus = "ALL";
|
||||
} else {
|
||||
this.searchForm.orderStatus = name;
|
||||
this.currentStatus = name;
|
||||
}
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.getDataList();
|
||||
},
|
||||
getOrderNumData() {
|
||||
API_Order.getOrderNum(this.countParams())
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
this.orderNumData = res.result;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.orderNumData = {};
|
||||
});
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.restoreQuery();
|
||||
},
|
||||
mounted() {
|
||||
this.init();
|
||||
},
|
||||
};
|
||||
})
|
||||
.catch(() => {
|
||||
orderNumData.value = {};
|
||||
});
|
||||
}
|
||||
|
||||
// created
|
||||
restoreQuery();
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -82,159 +82,162 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import * as API_floor from "@/api/other.js";
|
||||
export default {
|
||||
name: "floorList",
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
total: 0,
|
||||
formData: {
|
||||
status: false,
|
||||
name: "",
|
||||
},
|
||||
searchForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
pageType: "INDEX",
|
||||
pageClientType: "PC",
|
||||
},
|
||||
loading: false,
|
||||
pageTypes: [
|
||||
{ type: "INDEX", title: "首页" },
|
||||
{ type: "SPECIAL", title: "专题" },
|
||||
],
|
||||
list: [],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.getTemplateList();
|
||||
},
|
||||
methods: {
|
||||
newTemplate() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
const data = this.formData;
|
||||
data.status ? (data.pageShow = "OPEN") : (data.pageShow = "CLOSE");
|
||||
delete data.status;
|
||||
(data.pageType = this.searchForm.pageType),
|
||||
(data.pageClientType = "PC");
|
||||
if (data.id) {
|
||||
API_floor.updateHome(data.id, data).then((res) => {
|
||||
this.$Message.success("编辑模板成功");
|
||||
this.showModal = false;
|
||||
this.getTemplateList();
|
||||
});
|
||||
} else {
|
||||
API_floor.setHomeSetup(data).then((res) => {
|
||||
this.$Message.success("新建模板成功");
|
||||
this.showModal = false;
|
||||
this.getTemplateList();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.loading = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
import { Message, Modal } from "@/utils/message";
|
||||
|
||||
handleTabChange(type) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageType = type;
|
||||
this.getTemplateList();
|
||||
},
|
||||
defineOptions({ name: "floorList" });
|
||||
|
||||
createTemp() {
|
||||
this.$refs.form?.resetFields();
|
||||
this.formData = { status: false, name: "" };
|
||||
this.showModal = true;
|
||||
},
|
||||
const router = useRouter();
|
||||
|
||||
Template(item) {
|
||||
item.status = item.pageShow;
|
||||
this.formData = { ...item };
|
||||
this.showModal = true;
|
||||
},
|
||||
const showModal = ref(false);
|
||||
const total = ref(0);
|
||||
const formData = ref({
|
||||
status: false,
|
||||
name: "",
|
||||
});
|
||||
const searchForm = reactive({
|
||||
pageNumber: 1,
|
||||
pageSize: 20,
|
||||
sort: "createTime",
|
||||
order: "desc",
|
||||
pageType: "INDEX",
|
||||
pageClientType: "PC",
|
||||
});
|
||||
const loading = ref(false);
|
||||
const pageTypes = ref([
|
||||
{ type: "INDEX", title: "首页" },
|
||||
{ type: "SPECIAL", title: "专题" },
|
||||
]);
|
||||
const list = ref([]);
|
||||
|
||||
decorate(val) {
|
||||
const data = { id: val.id, pageShow: val.pageShow, pageType: this.searchForm.pageType };
|
||||
const form = ref(null);
|
||||
|
||||
this.$router.push({
|
||||
name: "renovation",
|
||||
query: data,
|
||||
});
|
||||
},
|
||||
onMounted(() => {
|
||||
getTemplateList();
|
||||
});
|
||||
|
||||
changePageNum(val) {
|
||||
this.searchForm.pageNumber = val;
|
||||
this.getTemplateList();
|
||||
},
|
||||
changePageSize(val) {
|
||||
this.searchForm.pageNumber = 1;
|
||||
this.searchForm.pageSize = val;
|
||||
this.getTemplateList();
|
||||
},
|
||||
getTemplateList() {
|
||||
this.loading = true;
|
||||
API_floor.getHomeList(this.searchForm).then((res) => {
|
||||
this.loading = false;
|
||||
if (res.success) {
|
||||
this.total = res.result.total;
|
||||
this.list = res.result.records;
|
||||
this.list.forEach((e) => {
|
||||
if (e.pageShow === "OPEN") {
|
||||
e.pageShow = true;
|
||||
} else {
|
||||
e.pageShow = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
beforeRelease(row) {
|
||||
if (row.pageShow) {
|
||||
this.$Message.warning(
|
||||
this.total === 1
|
||||
? "当前为唯一页面,无法关闭"
|
||||
: "请直接开启其他页面,当前页面会自动关闭"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
API_floor.releasePageHome(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
this.$Message.success("发布模板成功");
|
||||
resolve(true);
|
||||
this.getTemplateList();
|
||||
} else {
|
||||
reject(new Error("release failed"));
|
||||
}
|
||||
}).catch(() => {
|
||||
reject(new Error("release failed"));
|
||||
function newTemplate() {
|
||||
form.value.validate((valid) => {
|
||||
if (valid) {
|
||||
const data = formData.value;
|
||||
data.status ? (data.pageShow = "OPEN") : (data.pageShow = "CLOSE");
|
||||
delete data.status;
|
||||
(data.pageType = searchForm.pageType),
|
||||
(data.pageClientType = "PC");
|
||||
if (data.id) {
|
||||
API_floor.updateHome(data.id, data).then((res) => {
|
||||
Message.success("编辑模板成功");
|
||||
showModal.value = false;
|
||||
getTemplateList();
|
||||
});
|
||||
});
|
||||
},
|
||||
confirmDel(id) {
|
||||
this.$Modal.confirm({
|
||||
title: "提示",
|
||||
content: "删除此模板?",
|
||||
onOk: () => this.delTemplate(id),
|
||||
});
|
||||
},
|
||||
delTemplate(id) {
|
||||
API_floor.removePageHome(id).then((res) => {
|
||||
if (res.success) {
|
||||
this.$Message.success("删除模板成功");
|
||||
this.getTemplateList();
|
||||
} else {
|
||||
API_floor.setHomeSetup(data).then((res) => {
|
||||
Message.success("新建模板成功");
|
||||
showModal.value = false;
|
||||
getTemplateList();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleTabChange(type) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageType = type;
|
||||
getTemplateList();
|
||||
}
|
||||
|
||||
function createTemp() {
|
||||
form.value?.resetFields();
|
||||
formData.value = { status: false, name: "" };
|
||||
showModal.value = true;
|
||||
}
|
||||
|
||||
function Template(item) {
|
||||
item.status = item.pageShow;
|
||||
formData.value = { ...item };
|
||||
showModal.value = true;
|
||||
}
|
||||
|
||||
function decorate(val) {
|
||||
const data = { id: val.id, pageShow: val.pageShow, pageType: searchForm.pageType };
|
||||
|
||||
router.push({
|
||||
name: "renovation",
|
||||
query: data,
|
||||
});
|
||||
}
|
||||
|
||||
function changePageNum(val) {
|
||||
searchForm.pageNumber = val;
|
||||
getTemplateList();
|
||||
}
|
||||
function changePageSize(val) {
|
||||
searchForm.pageNumber = 1;
|
||||
searchForm.pageSize = val;
|
||||
getTemplateList();
|
||||
}
|
||||
function getTemplateList() {
|
||||
loading.value = true;
|
||||
API_floor.getHomeList(searchForm).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.success) {
|
||||
total.value = res.result.total;
|
||||
list.value = res.result.records;
|
||||
list.value.forEach((e) => {
|
||||
if (e.pageShow === "OPEN") {
|
||||
e.pageShow = true;
|
||||
} else {
|
||||
e.pageShow = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function beforeRelease(row) {
|
||||
if (row.pageShow) {
|
||||
Message.warning(
|
||||
total.value === 1
|
||||
? "当前为唯一页面,无法关闭"
|
||||
: "请直接开启其他页面,当前页面会自动关闭"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
API_floor.releasePageHome(row.id).then((res) => {
|
||||
if (res && res.success) {
|
||||
Message.success("发布模板成功");
|
||||
resolve(true);
|
||||
getTemplateList();
|
||||
} else {
|
||||
reject(new Error("release failed"));
|
||||
}
|
||||
}).catch(() => {
|
||||
reject(new Error("release failed"));
|
||||
});
|
||||
});
|
||||
}
|
||||
function confirmDel(id) {
|
||||
Modal.confirm({
|
||||
title: "提示",
|
||||
content: "删除此模板?",
|
||||
onOk: () => delTemplate(id),
|
||||
});
|
||||
}
|
||||
function delTemplate(id) {
|
||||
API_floor.removePageHome(id).then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("删除模板成功");
|
||||
getTemplateList();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -141,162 +141,174 @@
|
||||
<el-button type="primary" @click="handleConfirmNavModal">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
|
||||
<liliDialog ref="liliDialogRef" @selectedLink="selectedLink"></liliDialog>
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage
|
||||
@callback="callbackSelected"
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick, onMounted } from "vue";
|
||||
import { ShoppingCart } from "@element-plus/icons-vue";
|
||||
import Draggable from "vuedraggable";
|
||||
import ModelFormItem from "./modelFormItem.vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
import logoImg from "@/assets/logo.png";
|
||||
export default {
|
||||
name: "modelForm",
|
||||
components: {
|
||||
Draggable,
|
||||
ModelFormItem,
|
||||
ossManage,
|
||||
ShoppingCart,
|
||||
},
|
||||
props: ["data"],
|
||||
data() {
|
||||
return {
|
||||
logoImg,
|
||||
picModelFlag: false,
|
||||
showModal: false,
|
||||
showModalNav: false,
|
||||
navListBackup: null,
|
||||
draftTopAdvert: null,
|
||||
selectedNav: null,
|
||||
promotionTags: ["买2免1", "领200神券", "199减100", "母婴5折抢", "充100送20"],
|
||||
topAdvert: {
|
||||
type: "topAdvert",
|
||||
img: "",
|
||||
url: "",
|
||||
bgColor: "#de000d",
|
||||
size: "1200*80",
|
||||
},
|
||||
currentIndex: 0,
|
||||
navList: {
|
||||
type: "navBar",
|
||||
list: [
|
||||
{ name: "秒杀", url: "", title: "" },
|
||||
{ name: "闪购", url: "", title: "" },
|
||||
{ name: "优惠券", url: "", title: "" },
|
||||
{ name: "拍卖", url: "", title: "" },
|
||||
{ name: "服装城", url: "", title: "" },
|
||||
],
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
document.body.ondrop = function (event) {
|
||||
let isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
|
||||
if (isFirefox) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
getModelItemKey(element) {
|
||||
return element.model || element.key || element.type;
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "modelForm" });
|
||||
|
||||
const props = defineProps(["data"]);
|
||||
|
||||
const picModelFlag = ref(false);
|
||||
const showModal = ref(false);
|
||||
const showModalNav = ref(false);
|
||||
const navListBackup = ref(null);
|
||||
const draftTopAdvert = ref(null);
|
||||
const selectedNav = ref(null);
|
||||
const promotionTags = ref(["买2免1", "领200神券", "199减100", "母婴5折抢", "充100送20"]);
|
||||
const topAdvert = ref({
|
||||
type: "topAdvert",
|
||||
img: "",
|
||||
url: "",
|
||||
bgColor: "#de000d",
|
||||
size: "1200*80",
|
||||
});
|
||||
const currentIndex = ref(0);
|
||||
const navList = ref({
|
||||
type: "navBar",
|
||||
list: [
|
||||
{ name: "秒杀", url: "", title: "" },
|
||||
{ name: "闪购", url: "", title: "" },
|
||||
{ name: "优惠券", url: "", title: "" },
|
||||
{ name: "拍卖", url: "", title: "" },
|
||||
{ name: "服装城", url: "", title: "" },
|
||||
],
|
||||
});
|
||||
|
||||
const liliDialogRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
// 父组件 renovation.vue 通过 $refs.modelForm 读取并整体赋值 navList / topAdvert
|
||||
defineExpose({ navList, topAdvert });
|
||||
|
||||
onMounted(() => {
|
||||
document.body.ondrop = function (event) {
|
||||
let isFirefox = navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
|
||||
if (isFirefox) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
function getModelItemKey(element) {
|
||||
return element.model || element.key || element.type;
|
||||
}
|
||||
|
||||
function handleSelectLink(item, index) {
|
||||
if (item) selectedNav.value = item;
|
||||
liliDialogRef.value.open("link");
|
||||
currentIndex.value = index;
|
||||
}
|
||||
|
||||
function selectedLink(val) {
|
||||
if (showModalNav.value) {
|
||||
selectedNav.value.url = formatLinkType(val);
|
||||
selectedNav.value.type = val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
navList.value.list[currentIndex.value].title = val.title;
|
||||
} else if (showModal.value && draftTopAdvert.value) {
|
||||
draftTopAdvert.value.url = formatLinkType(val);
|
||||
draftTopAdvert.value.type = val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
}
|
||||
|
||||
function handleDelNav(index) {
|
||||
navList.value.list.splice(index, 1);
|
||||
}
|
||||
|
||||
function handleAddNav() {
|
||||
navList.value.list.push({ name: "", url: "", title: "" });
|
||||
}
|
||||
|
||||
function handleMoveEnd({ newIndex, oldIndex }) {}
|
||||
|
||||
function handleModel(type) {
|
||||
if (type == "topAdvert") {
|
||||
draftTopAdvert.value = JSON.parse(JSON.stringify(topAdvert.value));
|
||||
showModal.value = true;
|
||||
} else {
|
||||
navListBackup.value = JSON.parse(JSON.stringify(navList.value));
|
||||
showModalNav.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancelTopAdvertModal() {
|
||||
draftTopAdvert.value = null;
|
||||
showModal.value = false;
|
||||
}
|
||||
|
||||
function handleConfirmTopAdvertModal() {
|
||||
if (draftTopAdvert.value) {
|
||||
Object.assign(topAdvert.value, JSON.parse(JSON.stringify(draftTopAdvert.value)));
|
||||
}
|
||||
draftTopAdvert.value = null;
|
||||
showModal.value = false;
|
||||
}
|
||||
|
||||
function handleCancelNavModal() {
|
||||
showModalNav.value = false;
|
||||
}
|
||||
|
||||
function handleConfirmNavModal() {
|
||||
navListBackup.value = null;
|
||||
showModalNav.value = false;
|
||||
}
|
||||
|
||||
function handleNavDialogClose() {
|
||||
if (navListBackup.value) {
|
||||
navList.value = JSON.parse(JSON.stringify(navListBackup.value));
|
||||
navListBackup.value = null;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelectImg() {
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function callbackSelected(item) {
|
||||
picModelFlag.value = false;
|
||||
if (!item?.url || !draftTopAdvert.value) return;
|
||||
draftTopAdvert.value.img = item.url;
|
||||
}
|
||||
|
||||
function handleModelAdd(evt) {
|
||||
const newIndex = evt.newIndex;
|
||||
const current = props.data.list[newIndex];
|
||||
if (!current || current.key) {
|
||||
return;
|
||||
}
|
||||
const key = Date.now() + "_" + Math.ceil(Math.random() * 99999);
|
||||
props.data.list[newIndex] = {
|
||||
...JSON.parse(JSON.stringify(current)),
|
||||
options: {
|
||||
...current.options,
|
||||
},
|
||||
handleSelectLink(item, index) {
|
||||
if (item) this.selectedNav = item;
|
||||
this.$refs.liliDialog.open("link");
|
||||
this.currentIndex = index;
|
||||
},
|
||||
selectedLink(val) {
|
||||
if (this.showModalNav) {
|
||||
this.selectedNav.url = this.$filters.formatLinkType(val);
|
||||
this.selectedNav.type = val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
this.navList.list[this.currentIndex].title = val.title;
|
||||
} else if (this.showModal && this.draftTopAdvert) {
|
||||
this.draftTopAdvert.url = this.$filters.formatLinkType(val);
|
||||
this.draftTopAdvert.type = val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
},
|
||||
handleDelNav(index) {
|
||||
this.navList.list.splice(index, 1);
|
||||
},
|
||||
handleAddNav() {
|
||||
this.navList.list.push({ name: "", url: "", title: "" });
|
||||
},
|
||||
handleMoveEnd({ newIndex, oldIndex }) {},
|
||||
handleModel(type) {
|
||||
if (type == "topAdvert") {
|
||||
this.draftTopAdvert = JSON.parse(JSON.stringify(this.topAdvert));
|
||||
this.showModal = true;
|
||||
} else {
|
||||
this.navListBackup = JSON.parse(JSON.stringify(this.navList));
|
||||
this.showModalNav = true;
|
||||
}
|
||||
},
|
||||
handleCancelTopAdvertModal() {
|
||||
this.draftTopAdvert = null;
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmTopAdvertModal() {
|
||||
if (this.draftTopAdvert) {
|
||||
Object.assign(this.topAdvert, JSON.parse(JSON.stringify(this.draftTopAdvert)));
|
||||
}
|
||||
this.draftTopAdvert = null;
|
||||
this.showModal = false;
|
||||
},
|
||||
handleCancelNavModal() {
|
||||
this.showModalNav = false;
|
||||
},
|
||||
handleConfirmNavModal() {
|
||||
this.navListBackup = null;
|
||||
this.showModalNav = false;
|
||||
},
|
||||
handleNavDialogClose() {
|
||||
if (this.navListBackup) {
|
||||
this.navList = JSON.parse(JSON.stringify(this.navListBackup));
|
||||
this.navListBackup = null;
|
||||
}
|
||||
},
|
||||
handleSelectImg() {
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
callbackSelected(item) {
|
||||
this.picModelFlag = false;
|
||||
if (!item?.url || !this.draftTopAdvert) return;
|
||||
this.draftTopAdvert.img = item.url;
|
||||
},
|
||||
handleModelAdd(evt) {
|
||||
const newIndex = evt.newIndex;
|
||||
const current = this.data.list[newIndex];
|
||||
if (!current || current.key) {
|
||||
return;
|
||||
}
|
||||
const key = Date.now() + "_" + Math.ceil(Math.random() * 99999);
|
||||
this.data.list[newIndex] = {
|
||||
...JSON.parse(JSON.stringify(current)),
|
||||
options: {
|
||||
...current.options,
|
||||
},
|
||||
key,
|
||||
model: current.type + "_" + key,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
key,
|
||||
model: current.type + "_" + key,
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./modelList/setup-box.scss";
|
||||
|
||||
@@ -250,7 +250,7 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 选择商品。链接 -->
|
||||
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
|
||||
<liliDialog ref="liliDialogRef" @selectedLink="selectedLink"></liliDialog>
|
||||
<!-- 选择图片 -->
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage
|
||||
@@ -258,13 +258,14 @@
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import ModelCarousel from "./modelList/carousel.vue";
|
||||
import ModelCarousel1 from "./modelList/carousel1.vue";
|
||||
import ModelCarousel2 from "./modelList/carousel2.vue";
|
||||
@@ -283,98 +284,82 @@ import onlyGoodsModel from "./modelList/onlyGoodsModel.vue";
|
||||
import oneRowThreeColumns from "./modelList/oneRowThreeColumns.vue";
|
||||
import seckillOnlyAlbum from "./modelList/seckill-only-album.vue";
|
||||
import discountAdvertBg from "@/assets/nav/decorate.png";
|
||||
export default {
|
||||
name: "modelFormItem",
|
||||
props: ["element", "select", "index", "data"],
|
||||
components: {
|
||||
ModelCarousel,
|
||||
ModelCarousel1,
|
||||
ModelCarousel2,
|
||||
Recommend,
|
||||
NewGoodsSort,
|
||||
FirstPageAdvert,
|
||||
NotEnough,
|
||||
Seckill,
|
||||
goodsType,
|
||||
ossManage,
|
||||
oneRowThreeColumns,
|
||||
"seckill-only-album":seckillOnlyAlbum,
|
||||
onlyGoodsModel,
|
||||
mixModel,
|
||||
forYour
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
discountAdvertBg,
|
||||
showModal: false, // modal显隐
|
||||
selected: {}, // 弹窗编辑草稿
|
||||
editTarget: null, // 确认后写回的原数据
|
||||
picModelFlag: false, // 图片选择器
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
},
|
||||
// 编辑模块
|
||||
handleSelectModel(item) {
|
||||
this.editTarget = item;
|
||||
this.selected = this.cloneItem(item);
|
||||
this.showModal = true;
|
||||
},
|
||||
// 删除模块
|
||||
handleModelDelete() {
|
||||
this.$Modal.confirm({
|
||||
title: "提示",
|
||||
content: "<p>确定删除当前模块吗?</p>",
|
||||
onOk: () => {
|
||||
this.$nextTick(() => {
|
||||
this.data.list.splice(this.index, 1);
|
||||
});
|
||||
},
|
||||
});
|
||||
},
|
||||
handleSelectLink(item, index) {
|
||||
// 调起选择链接弹窗
|
||||
this.$refs.liliDialog.open("link");
|
||||
},
|
||||
// 确定选择链接
|
||||
selectedLink(val) {
|
||||
this.selected.url = this.$filters.formatLinkType(val);
|
||||
this.selected.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
import { Modal } from "@/utils/message";
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
handleSelectImg() {
|
||||
// 选择图片
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
defineOptions({ name: "modelFormItem" });
|
||||
|
||||
const props = defineProps(["element", "select", "index", "data"]);
|
||||
|
||||
const showModal = ref(false); // modal显隐
|
||||
const selected = ref({}); // 弹窗编辑草稿
|
||||
const editTarget = ref(null); // 确认后写回的原数据
|
||||
const picModelFlag = ref(false); // 图片选择器
|
||||
|
||||
// 模板 ref,与 import 同名会互相覆盖,故加 Ref 后缀
|
||||
const liliDialogRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
}
|
||||
// 编辑模块
|
||||
function handleSelectModel(item) {
|
||||
editTarget.value = item;
|
||||
selected.value = cloneItem(item);
|
||||
showModal.value = true;
|
||||
}
|
||||
// 删除模块
|
||||
function handleModelDelete() {
|
||||
Modal.confirm({
|
||||
title: "提示",
|
||||
content: "<p>确定删除当前模块吗?</p>",
|
||||
onOk: () => {
|
||||
nextTick(() => {
|
||||
props.data.list.splice(props.index, 1);
|
||||
});
|
||||
},
|
||||
// 回显图片
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (!val?.url) return;
|
||||
this.selected.img = val.url;
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
if (this.editTarget) {
|
||||
Object.assign(this.editTarget, this.cloneItem(this.selected));
|
||||
}
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
function handleSelectLink(item, index) {
|
||||
// 调起选择链接弹窗
|
||||
liliDialogRef.value.open("link");
|
||||
}
|
||||
// 确定选择链接
|
||||
function selectedLink(val) {
|
||||
selected.value.url = formatLinkType(val);
|
||||
selected.value.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
|
||||
function handleSelectImg() {
|
||||
// 选择图片
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
// 回显图片
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
if (!val?.url) return;
|
||||
selected.value.img = val.url;
|
||||
}
|
||||
function handleCancelModal() {
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleConfirmModal() {
|
||||
if (editTarget.value) {
|
||||
Object.assign(editTarget.value, cloneItem(selected.value));
|
||||
}
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal.value = false;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./modelList/setup-box.scss";
|
||||
|
||||
@@ -125,93 +125,91 @@
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
export default {
|
||||
name: "modelCarousel",
|
||||
props: ["data"],
|
||||
components: {
|
||||
ossManage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModal: false, // modal显隐
|
||||
selected: null, // 已选数据
|
||||
draftList: [], // 弹窗编辑草稿
|
||||
picModelFlag: false, // 选择图片modal
|
||||
userInfo: {},
|
||||
articleList: [
|
||||
{ title: "促销计算规则" },
|
||||
{ title: "商家申请开店" },
|
||||
{ title: "商家账号注册" },
|
||||
{ title: "促销计算规则" },
|
||||
],
|
||||
};
|
||||
},
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
methods: {
|
||||
cloneList(list) {
|
||||
return JSON.parse(JSON.stringify(list || []));
|
||||
},
|
||||
handleSelectModel() {
|
||||
this.draftList = this.cloneList(this.data.options.list);
|
||||
this.showModal = true;
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.draftList = [];
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
this.data.options.list.splice(
|
||||
0,
|
||||
this.data.options.list.length,
|
||||
...this.cloneList(this.draftList)
|
||||
);
|
||||
this.draftList = [];
|
||||
this.showModal = false;
|
||||
},
|
||||
// 添加轮播图
|
||||
handleAdd() {
|
||||
this.draftList.push({ img: "", url: "", type: "" });
|
||||
},
|
||||
handleSelectLink(item) {
|
||||
// 选择链接
|
||||
this.$refs.liliDialog.open("link");
|
||||
this.selected = item;
|
||||
},
|
||||
callbackSelected(item) {
|
||||
// 选择图片回调
|
||||
this.picModelFlag = false;
|
||||
if (!item?.url) return;
|
||||
this.selected.img = item.url;
|
||||
},
|
||||
handleDel(index) {
|
||||
this.draftList.splice(index, 1);
|
||||
},
|
||||
selectedLink(val) {
|
||||
// 选择链接回调
|
||||
this.selected.url = this.$filters.formatLinkType(val);
|
||||
this.selected.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
// 选择图片
|
||||
handleSelectImg(item) {
|
||||
this.selected = item;
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "modelCarousel" });
|
||||
|
||||
const props = defineProps(["data"]);
|
||||
|
||||
const showModal = ref(false); // modal显隐
|
||||
const selected = ref(null); // 已选数据
|
||||
const draftList = ref([]); // 弹窗编辑草稿
|
||||
const picModelFlag = ref(false); // 选择图片modal
|
||||
const userInfo = ref({});
|
||||
const articleList = ref([
|
||||
{ title: "促销计算规则" },
|
||||
{ title: "商家申请开店" },
|
||||
{ title: "商家账号注册" },
|
||||
{ title: "促销计算规则" },
|
||||
]);
|
||||
|
||||
// ossManageRef:模板 ref 与组件 import 同名,改名避免互相覆盖
|
||||
const liliDialog = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function cloneList(list) {
|
||||
return JSON.parse(JSON.stringify(list || []));
|
||||
}
|
||||
function handleSelectModel() {
|
||||
draftList.value = cloneList(props.data.options.list);
|
||||
showModal.value = true;
|
||||
}
|
||||
function handleCancelModal() {
|
||||
draftList.value = [];
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleConfirmModal() {
|
||||
props.data.options.list.splice(
|
||||
0,
|
||||
props.data.options.list.length,
|
||||
...cloneList(draftList.value)
|
||||
);
|
||||
draftList.value = [];
|
||||
showModal.value = false;
|
||||
}
|
||||
// 添加轮播图
|
||||
function handleAdd() {
|
||||
draftList.value.push({ img: "", url: "", type: "" });
|
||||
}
|
||||
function handleSelectLink(item) {
|
||||
// 选择链接
|
||||
liliDialog.value.open("link");
|
||||
selected.value = item;
|
||||
}
|
||||
function callbackSelected(item) {
|
||||
// 选择图片回调
|
||||
picModelFlag.value = false;
|
||||
if (!item?.url) return;
|
||||
selected.value.img = item.url;
|
||||
}
|
||||
function handleDel(index) {
|
||||
draftList.value.splice(index, 1);
|
||||
}
|
||||
function selectedLink(val) {
|
||||
// 选择链接回调
|
||||
selected.value.url = formatLinkType(val);
|
||||
selected.value.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
// 选择图片
|
||||
function handleSelectImg(item) {
|
||||
selected.value = item;
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -84,77 +84,81 @@
|
||||
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
|
||||
<!-- 选择图片 -->
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage @callback="callbackSelected" :is-component="true" :initialize="picModelFlag" :max-select="1" ref="ossManage" />
|
||||
<ossManage @callback="callbackSelected" :is-component="true" :initialize="picModelFlag" :max-select="1" ref="ossManageRef" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted, nextTick, getCurrentInstance } from "vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
export default {
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
defineOptions({
|
||||
name: "modelCarousel",
|
||||
props: ["data"],
|
||||
components: {
|
||||
ossManage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModal: false, // modal显隐
|
||||
selected: null, // 已选数据
|
||||
picModelFlag: false, // 选择图片modal
|
||||
bgColor: "#fff", // 轮播背景色
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.bgColor = this.data.options.list[0].bgColor;
|
||||
},
|
||||
methods: {
|
||||
handleSelectModel() {
|
||||
// 编辑模块
|
||||
this.showModal = true;
|
||||
},
|
||||
// 自动切换时改变背景色
|
||||
autoChange(oVal, val) {
|
||||
this.bgColor = this.data.options.list[val].bgColor;
|
||||
},
|
||||
// 添加轮播图片和链接
|
||||
handleAdd() {
|
||||
this.data.options.list.push({ img: "", url: "", bgColor: "#fff" });
|
||||
this.$forceUpdate();
|
||||
},
|
||||
// 打开选择链接modal
|
||||
handleSelectLink(item) {
|
||||
this.$refs.liliDialog.open("link");
|
||||
this.selected = item;
|
||||
},
|
||||
callbackSelected(item) {
|
||||
// 选择图片回调
|
||||
this.picModelFlag = false;
|
||||
if (!item?.url) return;
|
||||
this.selected.img = item.url;
|
||||
},
|
||||
// 删除图片
|
||||
handleDel(index) {
|
||||
this.data.options.list.splice(index, 1);
|
||||
},
|
||||
selectedLink(val) {
|
||||
// 选择链接回调
|
||||
this.selected.url = this.$filters.formatLinkType(val);
|
||||
this.selected.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
// 打开选择图片modal
|
||||
handleSelectImg(item) {
|
||||
this.selected = item;
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const props = defineProps(["data"]);
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const showModal = ref(false); // modal显隐
|
||||
const selected = ref(null); // 已选数据
|
||||
const picModelFlag = ref(false); // 选择图片modal
|
||||
const bgColor = ref("#fff"); // 轮播背景色
|
||||
|
||||
const liliDialog = ref(null);
|
||||
// ossManageRef:模板 ref 与组件 import 同名,改名避免互相覆盖
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
bgColor.value = props.data.options.list[0].bgColor;
|
||||
});
|
||||
|
||||
function handleSelectModel() {
|
||||
// 编辑模块
|
||||
showModal.value = true;
|
||||
}
|
||||
// 自动切换时改变背景色
|
||||
function autoChange(oVal, val) {
|
||||
bgColor.value = props.data.options.list[val].bgColor;
|
||||
}
|
||||
// 添加轮播图片和链接
|
||||
function handleAdd() {
|
||||
props.data.options.list.push({ img: "", url: "", bgColor: "#fff" });
|
||||
proxy.$forceUpdate();
|
||||
}
|
||||
// 打开选择链接modal
|
||||
function handleSelectLink(item) {
|
||||
liliDialog.value.open("link");
|
||||
selected.value = item;
|
||||
}
|
||||
function callbackSelected(item) {
|
||||
// 选择图片回调
|
||||
picModelFlag.value = false;
|
||||
if (!item?.url) return;
|
||||
selected.value.img = item.url;
|
||||
}
|
||||
// 删除图片
|
||||
function handleDel(index) {
|
||||
props.data.options.list.splice(index, 1);
|
||||
}
|
||||
function selectedLink(val) {
|
||||
// 选择链接回调
|
||||
selected.value.url = formatLinkType(val);
|
||||
selected.value.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
// 打开选择图片modal
|
||||
function handleSelectImg(item) {
|
||||
selected.value = item;
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -204,89 +204,89 @@
|
||||
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
|
||||
<!-- 选择图片 -->
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage @callback="callbackSelected" :is-component="true" :initialize="picModelFlag" :max-select="1" ref="ossManage" />
|
||||
<ossManage @callback="callbackSelected" :is-component="true" :initialize="picModelFlag" :max-select="1" ref="ossManageRef" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick, getCurrentInstance } from "vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
export default {
|
||||
name: "modelCarousel",
|
||||
props: ["data"],
|
||||
components: {
|
||||
ossManage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModal: false, // modal显隐
|
||||
selected: null, // 已选数据
|
||||
picModelFlag: false, // 选择图片modal
|
||||
userInfo: {},
|
||||
articleList: [
|
||||
{ title: "促销计算规则" },
|
||||
{ title: "商家申请开店" },
|
||||
{ title: "商家账号注册" },
|
||||
{ title: "促销计算规则" },
|
||||
],
|
||||
};
|
||||
},
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
methods: {
|
||||
handleSelectModel() {
|
||||
// 编辑模块
|
||||
this.showModal = true;
|
||||
},
|
||||
// 添加轮播
|
||||
handleAdd() {
|
||||
this.data.options.list.push({ img: "", url: "" });
|
||||
this.$forceUpdate();
|
||||
},
|
||||
// 添加图片组
|
||||
handleAddGroup() {
|
||||
this.data.options.listRight.push([
|
||||
{ img: "", url: "" },
|
||||
{ img: "", url: "" },
|
||||
{ img: "", url: "" },
|
||||
]);
|
||||
},
|
||||
// 删除图片组
|
||||
handleTabRemove(index) {
|
||||
this.data.options.listRight.splice(index, 1);
|
||||
},
|
||||
// 打开图片链接
|
||||
handleSelectLink(item) {
|
||||
this.$refs.liliDialog.open("link");
|
||||
this.selected = item;
|
||||
},
|
||||
callbackSelected(item) {
|
||||
// 选择图片回调
|
||||
this.picModelFlag = false;
|
||||
if (!item?.url) return;
|
||||
this.selected.img = item.url;
|
||||
},
|
||||
// 删除图片
|
||||
handleDel(index) {
|
||||
this.data.options.list.splice(index, 1);
|
||||
},
|
||||
selectedLink(val) {
|
||||
// 选择链接回调
|
||||
this.selected.url = this.$filters.formatLinkType(val);
|
||||
this.selected.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
// 打开选择图片modal
|
||||
handleSelectImg(item) {
|
||||
this.selected = item;
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
defineOptions({ name: "modelCarousel" });
|
||||
|
||||
const props = defineProps(["data"]);
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const showModal = ref(false); // modal显隐
|
||||
const selected = ref(null); // 已选数据
|
||||
const picModelFlag = ref(false); // 选择图片modal
|
||||
const userInfo = reactive({});
|
||||
const articleList = ref([
|
||||
{ title: "促销计算规则" },
|
||||
{ title: "商家申请开店" },
|
||||
{ title: "商家账号注册" },
|
||||
{ title: "促销计算规则" },
|
||||
]);
|
||||
|
||||
const liliDialog = ref(null);
|
||||
// ossManageRef:模板 ref 与组件 import 同名,改名避免互相覆盖
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function handleSelectModel() {
|
||||
// 编辑模块
|
||||
showModal.value = true;
|
||||
}
|
||||
// 添加轮播
|
||||
function handleAdd() {
|
||||
props.data.options.list.push({ img: "", url: "" });
|
||||
proxy.$forceUpdate();
|
||||
}
|
||||
// 添加图片组
|
||||
function handleAddGroup() {
|
||||
props.data.options.listRight.push([
|
||||
{ img: "", url: "" },
|
||||
{ img: "", url: "" },
|
||||
{ img: "", url: "" },
|
||||
]);
|
||||
}
|
||||
// 删除图片组
|
||||
function handleTabRemove(index) {
|
||||
props.data.options.listRight.splice(index, 1);
|
||||
}
|
||||
// 打开图片链接
|
||||
function handleSelectLink(item) {
|
||||
liliDialog.value.open("link");
|
||||
selected.value = item;
|
||||
}
|
||||
function callbackSelected(item) {
|
||||
// 选择图片回调
|
||||
picModelFlag.value = false;
|
||||
if (!item?.url) return;
|
||||
selected.value.img = item.url;
|
||||
}
|
||||
// 删除图片
|
||||
function handleDel(index) {
|
||||
props.data.options.list.splice(index, 1);
|
||||
}
|
||||
function selectedLink(val) {
|
||||
// 选择链接回调
|
||||
selected.value.url = formatLinkType(val);
|
||||
selected.value.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
// 打开选择图片modal
|
||||
function handleSelectImg(item) {
|
||||
selected.value = item;
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 选择商品。链接 -->
|
||||
<liliDialog ref="liliDialog" @selectedLink="selectedLink"></liliDialog>
|
||||
<liliDialog ref="liliDialogRef" @selectedLink="selectedLink"></liliDialog>
|
||||
<!-- 选择图片 -->
|
||||
<el-dialog v-model="picModelFlag" width="1200px" append-to-body destroy-on-close>
|
||||
<ossManage
|
||||
@@ -113,80 +113,79 @@
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
components: { ossManage },
|
||||
data() {
|
||||
return {
|
||||
options: this.data.options, // 当前类型数据
|
||||
showModal: false, // modal显隐
|
||||
selected: {}, // 弹窗编辑草稿
|
||||
editTarget: null, // 确认后写回的原数据
|
||||
picModelFlag: false, // 图片选择器
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
},
|
||||
// 打开装修modal
|
||||
handleSelectModel(item) {
|
||||
this.editTarget = item;
|
||||
this.selected = this.cloneItem(item);
|
||||
this.showModal = true;
|
||||
},
|
||||
handleSelectLink(item, index) {
|
||||
// 调起选择链接弹窗
|
||||
this.$refs.liliDialog.open("link");
|
||||
},
|
||||
// 选择链接回调
|
||||
selectedLink(val) {
|
||||
this.selected.url = this.$filters.formatLinkType(val);
|
||||
this.selected.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
handleSelectImg() {
|
||||
// 选择图片
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
// 选择图片回调
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (!val?.url) return;
|
||||
this.selected.img = val.url;
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
if (this.editTarget) {
|
||||
Object.assign(this.editTarget, this.cloneItem(this.selected));
|
||||
}
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const options = ref(props.data.options); // 当前类型数据
|
||||
const showModal = ref(false); // modal显隐
|
||||
const selected = ref({}); // 弹窗编辑草稿
|
||||
const editTarget = ref(null); // 确认后写回的原数据
|
||||
const picModelFlag = ref(false); // 图片选择器
|
||||
|
||||
const liliDialogRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
}
|
||||
// 打开装修modal
|
||||
function handleSelectModel(item) {
|
||||
editTarget.value = item;
|
||||
selected.value = cloneItem(item);
|
||||
showModal.value = true;
|
||||
}
|
||||
function handleSelectLink(item, index) {
|
||||
// 调起选择链接弹窗
|
||||
liliDialogRef.value.open("link");
|
||||
}
|
||||
// 选择链接回调
|
||||
function selectedLink(val) {
|
||||
selected.value.url = formatLinkType(val);
|
||||
selected.value.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
function handleSelectImg() {
|
||||
// 选择图片
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
// 选择图片回调
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
if (!val?.url) return;
|
||||
selected.value.img = val.url;
|
||||
}
|
||||
function handleCancelModal() {
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleConfirmModal() {
|
||||
if (editTarget.value) {
|
||||
Object.assign(editTarget.value, cloneItem(selected.value));
|
||||
}
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal.value = false;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./setup-box.scss";
|
||||
|
||||
@@ -174,117 +174,115 @@
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage.vue";
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
defineOptions({
|
||||
name: "forYour",
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
draftOptions: null,
|
||||
picModelFlag: false,
|
||||
selectedData: "", //选中的数据
|
||||
goodsIndex: 0, // 商品索引
|
||||
defaultCallbackImageType: "",
|
||||
};
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
ossManage,
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
cloneOptions(options) {
|
||||
return JSON.parse(JSON.stringify(options || {}));
|
||||
},
|
||||
handleSelectImg(type, index) {
|
||||
this.defaultCallbackImageType = type;
|
||||
this.goodsIndex = index;
|
||||
// 选择图片
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
handleSelectGoods(val, index) {
|
||||
this.$refs.liliDialog.open("goods");
|
||||
this.$refs.liliDialog.singleGoods();
|
||||
this.selectedData = val;
|
||||
this.goodsIndex = index;
|
||||
},
|
||||
handleSelectLink(val, index) {
|
||||
// 调起选择链接弹窗
|
||||
this.$refs.liliDialog.open("link");
|
||||
this.selectedData = val;
|
||||
|
||||
this.goodsIndex = index;
|
||||
},
|
||||
// 回显图片
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (!val?.url || !this.draftOptions) return;
|
||||
if (this.defaultCallbackImageType == "image") {
|
||||
this.draftOptions.data.image.src = val.url;
|
||||
} else {
|
||||
this.draftOptions.data[this.defaultCallbackImageType][
|
||||
this.goodsIndex
|
||||
].img = val.url;
|
||||
}
|
||||
},
|
||||
// 选择商品回调
|
||||
selectedGoodsData(val) {
|
||||
if (!val?.length || !this.draftOptions) return;
|
||||
const item = this.draftOptions.data.hot.list[this.goodsIndex];
|
||||
item.img = val[0].thumbnail;
|
||||
item.title = val[0].goodsName;
|
||||
item.price = val[0].price;
|
||||
item.url = this.$filters.formatLinkType(val[0]);
|
||||
},
|
||||
// 选择链接回调
|
||||
selectedLink(val) {
|
||||
if (!this.draftOptions) return;
|
||||
if (this.selectedData == "image") {
|
||||
this.draftOptions.data.image.url = this.$filters.formatLinkType(val);
|
||||
} else {
|
||||
this.draftOptions.data.list[this.goodsIndex].url =
|
||||
this.$filters.formatLinkType(val);
|
||||
}
|
||||
},
|
||||
handleSelectModel() {
|
||||
this.draftOptions = this.cloneOptions(this.data.options);
|
||||
this.showModal = true;
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.draftOptions = null;
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
if (this.draftOptions) {
|
||||
const saved = this.cloneOptions(this.draftOptions);
|
||||
this.data.options.title = saved.title;
|
||||
this.data.options.data = saved.data;
|
||||
}
|
||||
this.draftOptions = null;
|
||||
this.showModal = false;
|
||||
},
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const showModal = ref(false);
|
||||
const draftOptions = ref(null);
|
||||
const picModelFlag = ref(false);
|
||||
const selectedData = ref(""); //选中的数据
|
||||
const goodsIndex = ref(0); // 商品索引
|
||||
const defaultCallbackImageType = ref("");
|
||||
|
||||
const liliDialog = ref(null);
|
||||
// ossManageRef:模板 ref 与组件 import 同名,改名避免互相覆盖
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function cloneOptions(options) {
|
||||
return JSON.parse(JSON.stringify(options || {}));
|
||||
}
|
||||
function handleSelectImg(type, index) {
|
||||
defaultCallbackImageType.value = type;
|
||||
goodsIndex.value = index;
|
||||
// 选择图片
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleSelectGoods(val, index) {
|
||||
liliDialog.value.open("goods");
|
||||
liliDialog.value.singleGoods();
|
||||
selectedData.value = val;
|
||||
goodsIndex.value = index;
|
||||
}
|
||||
function handleSelectLink(val, index) {
|
||||
// 调起选择链接弹窗
|
||||
liliDialog.value.open("link");
|
||||
selectedData.value = val;
|
||||
|
||||
goodsIndex.value = index;
|
||||
}
|
||||
// 回显图片
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
if (!val?.url || !draftOptions.value) return;
|
||||
if (defaultCallbackImageType.value == "image") {
|
||||
draftOptions.value.data.image.src = val.url;
|
||||
} else {
|
||||
draftOptions.value.data[defaultCallbackImageType.value][
|
||||
goodsIndex.value
|
||||
].img = val.url;
|
||||
}
|
||||
}
|
||||
// 选择商品回调
|
||||
function selectedGoodsData(val) {
|
||||
if (!val?.length || !draftOptions.value) return;
|
||||
const item = draftOptions.value.data.hot.list[goodsIndex.value];
|
||||
item.img = val[0].thumbnail;
|
||||
item.title = val[0].goodsName;
|
||||
item.price = val[0].price;
|
||||
item.url = formatLinkType(val[0]);
|
||||
}
|
||||
// 选择链接回调
|
||||
function selectedLink(val) {
|
||||
if (!draftOptions.value) return;
|
||||
if (selectedData.value == "image") {
|
||||
draftOptions.value.data.image.url = formatLinkType(val);
|
||||
} else {
|
||||
draftOptions.value.data.list[goodsIndex.value].url =
|
||||
formatLinkType(val);
|
||||
}
|
||||
}
|
||||
function handleSelectModel() {
|
||||
draftOptions.value = cloneOptions(props.data.options);
|
||||
showModal.value = true;
|
||||
}
|
||||
function handleCancelModal() {
|
||||
draftOptions.value = null;
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleConfirmModal() {
|
||||
if (draftOptions.value) {
|
||||
const saved = cloneOptions(draftOptions.value);
|
||||
props.data.options.title = saved.title;
|
||||
props.data.options.data = saved.data;
|
||||
}
|
||||
draftOptions.value = null;
|
||||
showModal.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -127,137 +127,132 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, onMounted } from "vue";
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "goods",
|
||||
defineOptions({ name: "goods" });
|
||||
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabIndex: 0,
|
||||
current: 0,
|
||||
showModal: false,
|
||||
draftOptions: null,
|
||||
tabCurrentlyIndex: 0, // 选项卡索引
|
||||
};
|
||||
},
|
||||
mounted(){
|
||||
this.tabIndex = this.data.options.labels[0].___index
|
||||
},
|
||||
methods: {
|
||||
cloneOptions(options) {
|
||||
return JSON.parse(JSON.stringify(options || {}));
|
||||
},
|
||||
/**
|
||||
* 算出最大的index 然后索引叠加
|
||||
*/
|
||||
handleTabsAdd() {
|
||||
if (!this.draftOptions) return;
|
||||
const findAllIndex = this.draftOptions.labels.map((item) => item.___index);
|
||||
const max = Math.max.apply(null, findAllIndex);
|
||||
});
|
||||
|
||||
this.draftOptions.labels.push({
|
||||
label: "标签" + (max + 1),
|
||||
___index: max + 1,
|
||||
});
|
||||
},
|
||||
// 删除标签
|
||||
handleContextMenuDelete(item, index) {
|
||||
if (!this.draftOptions) return;
|
||||
this.draftOptions.list = this.draftOptions.list.filter(
|
||||
(lab) => lab.___index != item.___index
|
||||
);
|
||||
this.draftOptions.labels.splice(index, 1);
|
||||
},
|
||||
// 删除商品
|
||||
delGoods(val) {
|
||||
if (!this.draftOptions) return;
|
||||
this.draftOptions.list.forEach((item, i) => {
|
||||
if (
|
||||
item.title == val.title &&
|
||||
item.___index == this.tabCurrentlyIndex
|
||||
) {
|
||||
this.draftOptions.list.splice(i, 1);
|
||||
}
|
||||
});
|
||||
},
|
||||
// 切换选项卡
|
||||
handleClickTab(name) {
|
||||
this.tabCurrentlyIndex = name.paneName ?? name;
|
||||
},
|
||||
// 添加当前选项卡中的商品
|
||||
addCurrentGoods() {
|
||||
this.$refs.liliDialog.clearGoodsSelected();
|
||||
this.$refs.liliDialog.open("goods");
|
||||
},
|
||||
// 编辑模块
|
||||
handleSelectModel() {
|
||||
this.draftOptions = this.cloneOptions({
|
||||
title: this.data.options.title,
|
||||
labels: this.data.options.labels,
|
||||
list: this.data.options.list,
|
||||
});
|
||||
this.tabCurrentlyIndex = this.draftOptions.labels[0]?.___index ?? 0;
|
||||
this.tabIndex = String(this.tabCurrentlyIndex);
|
||||
this.showModal = true;
|
||||
},
|
||||
// 选择商品回调
|
||||
selectedGoodsData(val) {
|
||||
if (!val?.length || !this.draftOptions) return;
|
||||
val.forEach((item) => {
|
||||
this.draftOptions.list.push({
|
||||
img: item.thumbnail,
|
||||
price: item.price,
|
||||
title: item.goodsName,
|
||||
desc: "",
|
||||
url: `/goodsDetail?skuId=${item.id}&goodsId=${item.goodsId}`,
|
||||
___index: this.tabCurrentlyIndex,
|
||||
});
|
||||
});
|
||||
},
|
||||
// 选择链接回调
|
||||
selectedLink(val) {
|
||||
if (!this.draftOptions?.list?.length) return;
|
||||
const item = this.draftOptions.list[this.current];
|
||||
if (!item) return;
|
||||
item.url = this.$filters.formatLinkType(val);
|
||||
item.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
handleSelectLink(index) {
|
||||
this.$refs.liliDialog.open("link");
|
||||
this.current = index;
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.draftOptions = null;
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
if (this.draftOptions) {
|
||||
const saved = this.cloneOptions(this.draftOptions);
|
||||
this.data.options.title = saved.title;
|
||||
this.data.options.labels.splice(
|
||||
0,
|
||||
this.data.options.labels.length,
|
||||
...saved.labels
|
||||
);
|
||||
this.data.options.list.splice(
|
||||
0,
|
||||
this.data.options.list.length,
|
||||
...saved.list
|
||||
);
|
||||
}
|
||||
this.draftOptions = null;
|
||||
this.showModal = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
const tabIndex = ref(0);
|
||||
const current = ref(0);
|
||||
const showModal = ref(false);
|
||||
const draftOptions = ref(null);
|
||||
const tabCurrentlyIndex = ref(0); // 选项卡索引
|
||||
|
||||
const liliDialog = ref(null);
|
||||
|
||||
onMounted(() => {
|
||||
tabIndex.value = props.data.options.labels[0].___index;
|
||||
});
|
||||
|
||||
function cloneOptions(options) {
|
||||
return JSON.parse(JSON.stringify(options || {}));
|
||||
}
|
||||
/**
|
||||
* 算出最大的index 然后索引叠加
|
||||
*/
|
||||
function handleTabsAdd() {
|
||||
if (!draftOptions.value) return;
|
||||
const findAllIndex = draftOptions.value.labels.map((item) => item.___index);
|
||||
const max = Math.max.apply(null, findAllIndex);
|
||||
|
||||
draftOptions.value.labels.push({
|
||||
label: "标签" + (max + 1),
|
||||
___index: max + 1,
|
||||
});
|
||||
}
|
||||
// 删除标签
|
||||
function handleContextMenuDelete(item, index) {
|
||||
if (!draftOptions.value) return;
|
||||
draftOptions.value.list = draftOptions.value.list.filter(
|
||||
(lab) => lab.___index != item.___index
|
||||
);
|
||||
draftOptions.value.labels.splice(index, 1);
|
||||
}
|
||||
// 删除商品
|
||||
function delGoods(val) {
|
||||
if (!draftOptions.value) return;
|
||||
draftOptions.value.list.forEach((item, i) => {
|
||||
if (item.title == val.title && item.___index == tabCurrentlyIndex.value) {
|
||||
draftOptions.value.list.splice(i, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 切换选项卡
|
||||
function handleClickTab(name) {
|
||||
tabCurrentlyIndex.value = name.paneName ?? name;
|
||||
}
|
||||
// 添加当前选项卡中的商品
|
||||
function addCurrentGoods() {
|
||||
liliDialog.value.clearGoodsSelected();
|
||||
liliDialog.value.open("goods");
|
||||
}
|
||||
// 编辑模块
|
||||
function handleSelectModel() {
|
||||
draftOptions.value = cloneOptions({
|
||||
title: props.data.options.title,
|
||||
labels: props.data.options.labels,
|
||||
list: props.data.options.list,
|
||||
});
|
||||
tabCurrentlyIndex.value = draftOptions.value.labels[0]?.___index ?? 0;
|
||||
tabIndex.value = String(tabCurrentlyIndex.value);
|
||||
showModal.value = true;
|
||||
}
|
||||
// 选择商品回调
|
||||
function selectedGoodsData(val) {
|
||||
if (!val?.length || !draftOptions.value) return;
|
||||
val.forEach((item) => {
|
||||
draftOptions.value.list.push({
|
||||
img: item.thumbnail,
|
||||
price: item.price,
|
||||
title: item.goodsName,
|
||||
desc: "",
|
||||
url: `/goodsDetail?skuId=${item.id}&goodsId=${item.goodsId}`,
|
||||
___index: tabCurrentlyIndex.value,
|
||||
});
|
||||
});
|
||||
}
|
||||
// 选择链接回调
|
||||
function selectedLink(val) {
|
||||
if (!draftOptions.value?.list?.length) return;
|
||||
const item = draftOptions.value.list[current.value];
|
||||
if (!item) return;
|
||||
item.url = formatLinkType(val);
|
||||
item.type = val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
function handleSelectLink(index) {
|
||||
liliDialog.value.open("link");
|
||||
current.value = index;
|
||||
}
|
||||
function handleCancelModal() {
|
||||
draftOptions.value = null;
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleConfirmModal() {
|
||||
if (draftOptions.value) {
|
||||
const saved = cloneOptions(draftOptions.value);
|
||||
props.data.options.title = saved.title;
|
||||
props.data.options.labels.splice(
|
||||
0,
|
||||
props.data.options.labels.length,
|
||||
...saved.labels
|
||||
);
|
||||
props.data.options.list.splice(
|
||||
0,
|
||||
props.data.options.list.length,
|
||||
...saved.list
|
||||
);
|
||||
}
|
||||
draftOptions.value = null;
|
||||
showModal.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -138,7 +138,7 @@
|
||||
|
||||
<!-- 选择商品。链接 -->
|
||||
<liliDialog
|
||||
ref="liliDialog"
|
||||
ref="liliDialogRef"
|
||||
@selectedLink="selectedLink"
|
||||
@selectedGoodsData="selectedGoodsData"
|
||||
></liliDialog>
|
||||
@@ -150,119 +150,121 @@
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import left from "./mixs/mix-goods";
|
||||
import right from "./mixs/mix-brand";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage.vue";
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "mixModel",
|
||||
data() {
|
||||
return {
|
||||
showModal: false,
|
||||
current: null, // 当前编辑的模块草稿
|
||||
picModelFlag: false,
|
||||
selectedData: "", //选中的数据
|
||||
goodsIndex: 0, // 商品索引
|
||||
align: "",
|
||||
defaultCallbackImageType: "",
|
||||
};
|
||||
},
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
components: {
|
||||
left,
|
||||
right,
|
||||
ossManage,
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
cloneModule(module) {
|
||||
return JSON.parse(JSON.stringify(module || {}));
|
||||
},
|
||||
handleSelectImg(type, index) {
|
||||
this.defaultCallbackImageType = type;
|
||||
this.goodsIndex = index;
|
||||
// 选择图片
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
defineOptions({ name: "mixModel" });
|
||||
|
||||
handleSelectGoods(val, index) {
|
||||
this.$refs.liliDialog.open("goods");
|
||||
this.$refs.liliDialog.singleGoods();
|
||||
this.selectedData = val;
|
||||
this.goodsIndex = index;
|
||||
},
|
||||
handleSelectLink(val, index) {
|
||||
// 调起选择链接弹窗
|
||||
this.$refs.liliDialog.open("link");
|
||||
this.selectedData = val;
|
||||
this.goodsIndex = index;
|
||||
},
|
||||
// 回显图片
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (!val?.url || !this.current) return;
|
||||
if (this.defaultCallbackImageType == "default") {
|
||||
this.current.data.image.src = val.url;
|
||||
} else {
|
||||
this.current.data.list[this.goodsIndex].img = val.url;
|
||||
}
|
||||
},
|
||||
// 选择商品回调
|
||||
selectedGoodsData(val) {
|
||||
if (!val?.length || !this.current) return;
|
||||
const goods = val[0];
|
||||
const item = this.current.data.list[this.goodsIndex];
|
||||
item.img = goods.thumbnail;
|
||||
item.title = goods.goodsName;
|
||||
item.price = goods.price;
|
||||
item.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
},
|
||||
// 选择链接回调
|
||||
selectedLink(val) {
|
||||
if (!this.current) return;
|
||||
const formattedUrl = this.$filters.formatLinkType(val);
|
||||
if (this.selectedData != "list") {
|
||||
this.current.data[this.selectedData].url = formattedUrl;
|
||||
}
|
||||
if (this.selectedData == "list") {
|
||||
this.current.data.list[this.goodsIndex].url = formattedUrl;
|
||||
}
|
||||
},
|
||||
handleSelectModel(align) {
|
||||
this.align = align;
|
||||
this.current = this.cloneModule(this.data.options[align]);
|
||||
this.showModal = true;
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.current = null;
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
if (this.current && this.align) {
|
||||
this.data.options[this.align] = this.cloneModule(this.current);
|
||||
}
|
||||
this.current = null;
|
||||
this.showModal = false;
|
||||
},
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const showModal = ref(false);
|
||||
const current = ref(null); // 当前编辑的模块草稿
|
||||
const picModelFlag = ref(false);
|
||||
const selectedData = ref(""); //选中的数据
|
||||
const goodsIndex = ref(0); // 商品索引
|
||||
const align = ref("");
|
||||
const defaultCallbackImageType = ref("");
|
||||
|
||||
const liliDialogRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function cloneModule(module) {
|
||||
return JSON.parse(JSON.stringify(module || {}));
|
||||
}
|
||||
|
||||
function handleSelectImg(type, index) {
|
||||
defaultCallbackImageType.value = type;
|
||||
goodsIndex.value = index;
|
||||
// 选择图片
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleSelectGoods(val, index) {
|
||||
liliDialogRef.value.open("goods");
|
||||
liliDialogRef.value.singleGoods();
|
||||
selectedData.value = val;
|
||||
goodsIndex.value = index;
|
||||
}
|
||||
|
||||
function handleSelectLink(val, index) {
|
||||
// 调起选择链接弹窗
|
||||
liliDialogRef.value.open("link");
|
||||
selectedData.value = val;
|
||||
goodsIndex.value = index;
|
||||
}
|
||||
|
||||
// 回显图片
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
if (!val?.url || !current.value) return;
|
||||
if (defaultCallbackImageType.value == "default") {
|
||||
current.value.data.image.src = val.url;
|
||||
} else {
|
||||
current.value.data.list[goodsIndex.value].img = val.url;
|
||||
}
|
||||
}
|
||||
|
||||
// 选择商品回调
|
||||
function selectedGoodsData(val) {
|
||||
if (!val?.length || !current.value) return;
|
||||
const goods = val[0];
|
||||
const item = current.value.data.list[goodsIndex.value];
|
||||
item.img = goods.thumbnail;
|
||||
item.title = goods.goodsName;
|
||||
item.price = goods.price;
|
||||
item.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
}
|
||||
|
||||
// 选择链接回调
|
||||
function selectedLink(val) {
|
||||
if (!current.value) return;
|
||||
const formattedUrl = formatLinkType(val);
|
||||
if (selectedData.value != "list") {
|
||||
current.value.data[selectedData.value].url = formattedUrl;
|
||||
}
|
||||
if (selectedData.value == "list") {
|
||||
current.value.data.list[goodsIndex.value].url = formattedUrl;
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelectModel(type) {
|
||||
align.value = type;
|
||||
current.value = cloneModule(props.data.options[type]);
|
||||
showModal.value = true;
|
||||
}
|
||||
|
||||
function handleCancelModal() {
|
||||
current.value = null;
|
||||
showModal.value = false;
|
||||
}
|
||||
|
||||
function handleConfirmModal() {
|
||||
if (current.value && align.value) {
|
||||
props.data.options[align.value] = cloneModule(current.value);
|
||||
}
|
||||
current.value = null;
|
||||
showModal.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -24,19 +24,17 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ArrowRight } from "@element-plus/icons-vue";
|
||||
|
||||
export default {
|
||||
name: "mix-brand",
|
||||
components: { ArrowRight },
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
defineOptions({ name: "mix-brand" });
|
||||
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -28,16 +28,17 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
<script setup>
|
||||
defineOptions({
|
||||
name: "mix-goods",
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
|
||||
defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -242,125 +242,123 @@
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
components: {
|
||||
ossManage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: this.data.options, // 当前数据
|
||||
showModal: false, // modal显隐
|
||||
showModal1: false, // modal显隐
|
||||
selected: {}, // 弹窗编辑草稿
|
||||
editTarget: null, // 确认后写回的原数据
|
||||
picModelFlag: false, // 选择图片modal
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
},
|
||||
// 装修modal
|
||||
handleSelectModel(item, type) {
|
||||
this.editTarget = item;
|
||||
this.selected = this.cloneItem(item);
|
||||
if (type) {
|
||||
this.showModal1 = true;
|
||||
} else {
|
||||
this.showModal = true;
|
||||
}
|
||||
},
|
||||
handleSelectLink() {
|
||||
// 调起选择链接弹窗
|
||||
this.$refs.liliDialog.open("link");
|
||||
},
|
||||
handleSelectGoods(item) {
|
||||
if (item) {
|
||||
this.editTarget = item;
|
||||
this.selected = this.cloneItem(item);
|
||||
}
|
||||
this.$refs.liliDialog.open("goods", "single");
|
||||
setTimeout(() => {
|
||||
this.$refs.liliDialog.goodsData = [this.selected];
|
||||
}, 500);
|
||||
},
|
||||
// 选择链接回调
|
||||
selectedLink(val) {
|
||||
if (!this.selected) return;
|
||||
this.selected.url = this.$filters.formatLinkType(val);
|
||||
this.selected.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
// 选择商品回调
|
||||
selectedGoodsData(val) {
|
||||
if (!val?.length || !this.selected) return;
|
||||
const goods = val[0];
|
||||
this.selected.img = goods.thumbnail;
|
||||
this.selected.price = goods.price;
|
||||
this.selected.name = goods.goodsName;
|
||||
this.selected.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
if (this.editTarget && !this.showModal && !this.showModal1) {
|
||||
Object.assign(this.editTarget, this.cloneItem(this.selected));
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
}
|
||||
},
|
||||
handleSelectImg() {
|
||||
// 选择图片
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
// 选择图片回显
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (!val?.url) return;
|
||||
this.selected.img = val.url;
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
if (this.editTarget) {
|
||||
Object.assign(this.editTarget, this.cloneItem(this.selected));
|
||||
}
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal = false;
|
||||
},
|
||||
handleCancelModal1() {
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal1 = false;
|
||||
},
|
||||
handleConfirmModal1() {
|
||||
if (this.editTarget) {
|
||||
Object.assign(this.editTarget, this.cloneItem(this.selected));
|
||||
}
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal1 = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const options = ref(props.data.options); // 当前数据
|
||||
const showModal = ref(false); // modal显隐
|
||||
const showModal1 = ref(false); // modal显隐
|
||||
const selected = ref({}); // 弹窗编辑草稿
|
||||
const editTarget = ref(null); // 确认后写回的原数据
|
||||
const picModelFlag = ref(false); // 选择图片modal
|
||||
|
||||
// ossManageRef:模板 ref 与组件 import 同名,改名避免互相覆盖
|
||||
const liliDialog = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
}
|
||||
// 装修modal
|
||||
function handleSelectModel(item, type) {
|
||||
editTarget.value = item;
|
||||
selected.value = cloneItem(item);
|
||||
if (type) {
|
||||
showModal1.value = true;
|
||||
} else {
|
||||
showModal.value = true;
|
||||
}
|
||||
}
|
||||
function handleSelectLink() {
|
||||
// 调起选择链接弹窗
|
||||
liliDialog.value.open("link");
|
||||
}
|
||||
function handleSelectGoods(item) {
|
||||
if (item) {
|
||||
editTarget.value = item;
|
||||
selected.value = cloneItem(item);
|
||||
}
|
||||
liliDialog.value.open("goods", "single");
|
||||
setTimeout(() => {
|
||||
liliDialog.value.goodsData = [selected.value];
|
||||
}, 500);
|
||||
}
|
||||
// 选择链接回调
|
||||
function selectedLink(val) {
|
||||
if (!selected.value) return;
|
||||
selected.value.url = formatLinkType(val);
|
||||
selected.value.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
// 选择商品回调
|
||||
function selectedGoodsData(val) {
|
||||
if (!val?.length || !selected.value) return;
|
||||
const goods = val[0];
|
||||
selected.value.img = goods.thumbnail;
|
||||
selected.value.price = goods.price;
|
||||
selected.value.name = goods.goodsName;
|
||||
selected.value.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
if (editTarget.value && !showModal.value && !showModal1.value) {
|
||||
Object.assign(editTarget.value, cloneItem(selected.value));
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
}
|
||||
}
|
||||
function handleSelectImg() {
|
||||
// 选择图片
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
// 选择图片回显
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
if (!val?.url) return;
|
||||
selected.value.img = val.url;
|
||||
}
|
||||
function handleCancelModal() {
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleConfirmModal() {
|
||||
if (editTarget.value) {
|
||||
Object.assign(editTarget.value, cloneItem(selected.value));
|
||||
}
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleCancelModal1() {
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal1.value = false;
|
||||
}
|
||||
function handleConfirmModal1() {
|
||||
if (editTarget.value) {
|
||||
Object.assign(editTarget.value, cloneItem(selected.value));
|
||||
}
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal1.value = false;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./setup-box.scss";
|
||||
|
||||
@@ -96,118 +96,116 @@
|
||||
></liliDialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props:{
|
||||
data:{
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentIndex:0,
|
||||
conData:this.data,
|
||||
selected:{},
|
||||
editTarget: null,
|
||||
showModal:false,
|
||||
showGoodsModal: false,
|
||||
showContent:true,
|
||||
draftNavList: [],
|
||||
draftGoodsList: [],
|
||||
}
|
||||
},
|
||||
watch:{
|
||||
data:function(val){
|
||||
this.conData = val
|
||||
},
|
||||
conData:function(val){
|
||||
this.$emit('content',val)
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
cloneList(list) {
|
||||
return JSON.parse(JSON.stringify(list || []));
|
||||
},
|
||||
cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
},
|
||||
changeCurr(index){
|
||||
this.currentIndex = index;
|
||||
},
|
||||
handleSelectModel () {
|
||||
this.draftNavList = this.cloneList(this.conData.options.navList);
|
||||
this.draftGoodsList = this.cloneList(this.conData.options.list);
|
||||
this.showModal = true;
|
||||
},
|
||||
handleSelectGoods(item) {
|
||||
this.editTarget = item;
|
||||
this.selected = this.cloneItem(item);
|
||||
this.showGoodsModal = true;
|
||||
},
|
||||
handleOpenGoodsPicker() {
|
||||
this.$refs.liliDialog.open('goods', 'single');
|
||||
setTimeout(() => {
|
||||
this.$refs.liliDialog.goodsData = [this.selected];
|
||||
}, 500);
|
||||
},
|
||||
selectedGoodsData(val){
|
||||
if (!val?.length) return;
|
||||
const goods = val[0];
|
||||
this.selected.img = goods.thumbnail;
|
||||
this.selected.price = goods.price;
|
||||
this.selected.name = goods.goodsName;
|
||||
this.selected.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
},
|
||||
handleDelNav(index){
|
||||
this.draftNavList.splice(index,1);
|
||||
this.draftGoodsList.splice(index,1);
|
||||
},
|
||||
handleAddNav(){
|
||||
this.draftNavList.push({ title:'', desc:'' });
|
||||
this.draftGoodsList.push([
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
]);
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.draftNavList = [];
|
||||
this.draftGoodsList = [];
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
this.conData.options.navList = this.cloneList(this.draftNavList);
|
||||
this.conData.options.list = this.cloneList(this.draftGoodsList);
|
||||
this.draftNavList = [];
|
||||
this.draftGoodsList = [];
|
||||
this.showContent = false;
|
||||
this.$nextTick(() => {
|
||||
this.showContent = true;
|
||||
});
|
||||
this.showModal = false;
|
||||
},
|
||||
handleCancelGoodsModal() {
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showGoodsModal = false;
|
||||
},
|
||||
handleConfirmGoodsModal() {
|
||||
if (this.editTarget) {
|
||||
Object.assign(this.editTarget, this.cloneItem(this.selected));
|
||||
}
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showGoodsModal = false;
|
||||
},
|
||||
<script setup>
|
||||
import { ref, watch, nextTick } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
data:{
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
});
|
||||
const emit = defineEmits(['content']);
|
||||
|
||||
const currentIndex = ref(0);
|
||||
const conData = ref(props.data);
|
||||
const selected = ref({});
|
||||
const editTarget = ref(null);
|
||||
const showModal = ref(false);
|
||||
const showGoodsModal = ref(false);
|
||||
const showContent = ref(true);
|
||||
const draftNavList = ref([]);
|
||||
const draftGoodsList = ref([]);
|
||||
|
||||
const liliDialog = ref(null);
|
||||
|
||||
watch(() => props.data, function(val){
|
||||
conData.value = val
|
||||
});
|
||||
watch(conData, function(val){
|
||||
emit('content', val)
|
||||
});
|
||||
|
||||
function cloneList(list) {
|
||||
return JSON.parse(JSON.stringify(list || []));
|
||||
}
|
||||
function cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
}
|
||||
function changeCurr(index){
|
||||
currentIndex.value = index;
|
||||
}
|
||||
function handleSelectModel () {
|
||||
draftNavList.value = cloneList(conData.value.options.navList);
|
||||
draftGoodsList.value = cloneList(conData.value.options.list);
|
||||
showModal.value = true;
|
||||
}
|
||||
function handleSelectGoods(item) {
|
||||
editTarget.value = item;
|
||||
selected.value = cloneItem(item);
|
||||
showGoodsModal.value = true;
|
||||
}
|
||||
function handleOpenGoodsPicker() {
|
||||
liliDialog.value.open('goods', 'single');
|
||||
setTimeout(() => {
|
||||
liliDialog.value.goodsData = [selected.value];
|
||||
}, 500);
|
||||
}
|
||||
function selectedGoodsData(val){
|
||||
if (!val?.length) return;
|
||||
const goods = val[0];
|
||||
selected.value.img = goods.thumbnail;
|
||||
selected.value.price = goods.price;
|
||||
selected.value.name = goods.goodsName;
|
||||
selected.value.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
}
|
||||
function handleDelNav(index){
|
||||
draftNavList.value.splice(index,1);
|
||||
draftGoodsList.value.splice(index,1);
|
||||
}
|
||||
function handleAddNav(){
|
||||
draftNavList.value.push({ title:'', desc:'' });
|
||||
draftGoodsList.value.push([
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
{ img:'', name:'', price:0, url:'' },
|
||||
]);
|
||||
}
|
||||
function handleCancelModal() {
|
||||
draftNavList.value = [];
|
||||
draftGoodsList.value = [];
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleConfirmModal() {
|
||||
conData.value.options.navList = cloneList(draftNavList.value);
|
||||
conData.value.options.list = cloneList(draftGoodsList.value);
|
||||
draftNavList.value = [];
|
||||
draftGoodsList.value = [];
|
||||
showContent.value = false;
|
||||
nextTick(() => {
|
||||
showContent.value = true;
|
||||
});
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleCancelGoodsModal() {
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showGoodsModal.value = false;
|
||||
}
|
||||
function handleConfirmGoodsModal() {
|
||||
if (editTarget.value) {
|
||||
Object.assign(editTarget.value, cloneItem(selected.value));
|
||||
}
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showGoodsModal.value = false;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
</el-dialog>
|
||||
<!-- 选择商品。链接 -->
|
||||
<liliDialog
|
||||
ref="liliDialog"
|
||||
ref="liliDialogRef"
|
||||
@selectedLink="selectedLink"
|
||||
@selectedGoodsData="selectedGoodsData"
|
||||
></liliDialog>
|
||||
@@ -60,89 +60,89 @@
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
export default {
|
||||
name: "oneRowThreeColumns",
|
||||
components:{ossManage},
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showModal:false,
|
||||
draftList: [],
|
||||
picModelFlag:false,
|
||||
current:0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
cloneList(list) {
|
||||
return JSON.parse(JSON.stringify(list || []));
|
||||
},
|
||||
// 回显图片
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (!val?.url) return;
|
||||
this.draftList[this.current].img = val.url;
|
||||
},
|
||||
// 编辑模块
|
||||
handleSelectModel() {
|
||||
this.draftList = this.cloneList(this.data.options.list);
|
||||
this.showModal = true;
|
||||
},
|
||||
// 选择商品回调
|
||||
selectedGoodsData(val) {
|
||||
const goods = val[0];
|
||||
if (!goods) return;
|
||||
const item = this.draftList[this.current];
|
||||
item.img = goods.thumbnail;
|
||||
item.price = goods.price;
|
||||
item.name = goods.goodsName;
|
||||
item.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
},
|
||||
// 选择链接回调
|
||||
selectedLink(val) {
|
||||
const item = this.draftList[this.current];
|
||||
item.url = this.$filters.formatLinkType(val);
|
||||
item.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
handleSelectLink(index) {
|
||||
// 调起选择链接弹窗
|
||||
this.$refs.liliDialog.open("link");
|
||||
this.current = index;
|
||||
},
|
||||
handleSelectImg(index){
|
||||
// 选择图片
|
||||
this.current = index;
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.draftList = [];
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
const saved = this.cloneList(this.draftList);
|
||||
this.data.options.list.splice(0, this.data.options.list.length, ...saved);
|
||||
this.draftList = [];
|
||||
this.showModal = false;
|
||||
},
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
defineOptions({ name: "oneRowThreeColumns" });
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
});
|
||||
|
||||
const showModal = ref(false);
|
||||
const draftList = ref([]);
|
||||
const picModelFlag = ref(false);
|
||||
const current = ref(0);
|
||||
|
||||
const liliDialogRef = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function cloneList(list) {
|
||||
return JSON.parse(JSON.stringify(list || []));
|
||||
}
|
||||
// 回显图片
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
if (!val?.url) return;
|
||||
draftList.value[current.value].img = val.url;
|
||||
}
|
||||
// 编辑模块
|
||||
function handleSelectModel() {
|
||||
draftList.value = cloneList(props.data.options.list);
|
||||
showModal.value = true;
|
||||
}
|
||||
// 选择商品回调
|
||||
function selectedGoodsData(val) {
|
||||
const goods = val[0];
|
||||
if (!goods) return;
|
||||
const item = draftList.value[current.value];
|
||||
item.img = goods.thumbnail;
|
||||
item.price = goods.price;
|
||||
item.name = goods.goodsName;
|
||||
item.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
}
|
||||
// 选择链接回调
|
||||
function selectedLink(val) {
|
||||
const item = draftList.value[current.value];
|
||||
item.url = formatLinkType(val);
|
||||
item.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
function handleSelectLink(index) {
|
||||
// 调起选择链接弹窗
|
||||
liliDialogRef.value.open("link");
|
||||
current.value = index;
|
||||
}
|
||||
function handleSelectImg(index) {
|
||||
// 选择图片
|
||||
current.value = index;
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
function handleCancelModal() {
|
||||
draftList.value = [];
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleConfirmModal() {
|
||||
const saved = cloneList(draftList.value);
|
||||
props.data.options.list.splice(0, props.data.options.list.length, ...saved);
|
||||
draftList.value = [];
|
||||
showModal.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -80,92 +80,97 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
export default {
|
||||
name: "onlyGoodsList",
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
showModal: false,
|
||||
draftList: [],
|
||||
};
|
||||
},
|
||||
mounted(){
|
||||
defineOptions({ name: "onlyGoodsList" });
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
methods: {
|
||||
cloneList(list) {
|
||||
return JSON.parse(JSON.stringify(list || []));
|
||||
},
|
||||
// 删除商品
|
||||
delGoods(val, i) {
|
||||
this.draftList.splice(i, 1);
|
||||
},
|
||||
// 添加当前选项卡中的商品
|
||||
addCurrentGoods() {
|
||||
this.$refs.liliDialog.open("goods");
|
||||
},
|
||||
// 编辑模块
|
||||
handleSelectModel() {
|
||||
this.draftList = this.cloneList(this.data.options.list);
|
||||
this.showModal = true;
|
||||
},
|
||||
// 选择商品回调
|
||||
selectedGoodsData(val) {
|
||||
if (!val?.length || !this.draftList) return;
|
||||
val.forEach((item) => {
|
||||
if (this.isGoodsInDraftList(item)) return;
|
||||
this.draftList.push({
|
||||
img: item.thumbnail,
|
||||
price: item.price,
|
||||
title: item.goodsName,
|
||||
desc: "",
|
||||
skuId: item.id,
|
||||
goodsId: item.goodsId,
|
||||
url: `/goodsDetail?skuId=${item.id}&goodsId=${item.goodsId}`,
|
||||
});
|
||||
});
|
||||
},
|
||||
isGoodsInDraftList(goods) {
|
||||
const targetUrl = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
return this.draftList.some(
|
||||
(draft) =>
|
||||
(draft.skuId && String(draft.skuId) === String(goods.id)) ||
|
||||
(draft.url && draft.url === targetUrl)
|
||||
);
|
||||
},
|
||||
// 选择链接回调
|
||||
selectedLink(val) {
|
||||
if (!this.draftList?.length) return;
|
||||
const item = this.draftList[this.current];
|
||||
item.url = this.$filters.formatLinkType(val);
|
||||
item.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
handleSelectLink(index) {
|
||||
// 调起选择链接弹窗
|
||||
this.$refs.liliDialog.open("link");
|
||||
this.current = index;
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.draftList = [];
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
const saved = this.cloneList(this.draftList);
|
||||
this.data.options.list.splice(0, this.data.options.list.length, ...saved);
|
||||
this.draftList = [];
|
||||
this.showModal = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const current = ref(0);
|
||||
const showModal = ref(false);
|
||||
const draftList = ref([]);
|
||||
|
||||
const liliDialog = ref(null);
|
||||
|
||||
function cloneList(list) {
|
||||
return JSON.parse(JSON.stringify(list || []));
|
||||
}
|
||||
|
||||
// 删除商品
|
||||
function delGoods(val, i) {
|
||||
draftList.value.splice(i, 1);
|
||||
}
|
||||
|
||||
// 添加当前选项卡中的商品
|
||||
function addCurrentGoods() {
|
||||
liliDialog.value.open("goods");
|
||||
}
|
||||
|
||||
// 编辑模块
|
||||
function handleSelectModel() {
|
||||
draftList.value = cloneList(props.data.options.list);
|
||||
showModal.value = true;
|
||||
}
|
||||
|
||||
// 选择商品回调
|
||||
function selectedGoodsData(val) {
|
||||
if (!val?.length || !draftList.value) return;
|
||||
val.forEach((item) => {
|
||||
if (isGoodsInDraftList(item)) return;
|
||||
draftList.value.push({
|
||||
img: item.thumbnail,
|
||||
price: item.price,
|
||||
title: item.goodsName,
|
||||
desc: "",
|
||||
skuId: item.id,
|
||||
goodsId: item.goodsId,
|
||||
url: `/goodsDetail?skuId=${item.id}&goodsId=${item.goodsId}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function isGoodsInDraftList(goods) {
|
||||
const targetUrl = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
return draftList.value.some(
|
||||
(draft) =>
|
||||
(draft.skuId && String(draft.skuId) === String(goods.id)) ||
|
||||
(draft.url && draft.url === targetUrl)
|
||||
);
|
||||
}
|
||||
|
||||
// 选择链接回调
|
||||
function selectedLink(val) {
|
||||
if (!draftList.value?.length) return;
|
||||
const item = draftList.value[current.value];
|
||||
item.url = formatLinkType(val);
|
||||
item.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
|
||||
function handleSelectLink(index) {
|
||||
// 调起选择链接弹窗
|
||||
liliDialog.value.open("link");
|
||||
current.value = index;
|
||||
}
|
||||
|
||||
function handleCancelModal() {
|
||||
draftList.value = [];
|
||||
showModal.value = false;
|
||||
}
|
||||
|
||||
function handleConfirmModal() {
|
||||
const saved = cloneList(draftList.value);
|
||||
props.data.options.list.splice(0, props.data.options.list.length, ...saved);
|
||||
draftList.value = [];
|
||||
showModal.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -227,113 +227,111 @@
|
||||
:is-component="true"
|
||||
:initialize="picModelFlag"
|
||||
:max-select="1"
|
||||
ref="ossManage"
|
||||
ref="ossManageRef"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script setup>
|
||||
import { ref, nextTick } from "vue";
|
||||
import ossManage from "@/views/sys/oss-manage/ossManage";
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
import { formatLinkType } from "@/utils/filters";
|
||||
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: {}, // 历史遗留:对象默认值未用工厂函数,原样保留
|
||||
},
|
||||
components: {
|
||||
ossManage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
msgLeft: this.data.options.contentLeft, // 左侧数据
|
||||
msgRight: this.data.options.contentRight, // 右侧数据
|
||||
showModal: false, // modal显隐
|
||||
showModal1: false, // modal显隐
|
||||
selected: {}, // 弹窗编辑草稿
|
||||
editTarget: null, // 确认后写回的原数据
|
||||
picModelFlag: false, // 图片选择
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
},
|
||||
// 编辑
|
||||
handleSelectModel(item, type) {
|
||||
this.editTarget = item;
|
||||
this.selected = this.cloneItem(item);
|
||||
if (type) {
|
||||
this.showModal1 = true;
|
||||
} else {
|
||||
this.showModal = true;
|
||||
}
|
||||
},
|
||||
handleSelectLink(item, index) {
|
||||
// 调起选择链接弹窗
|
||||
this.$refs.liliDialog.open("link");
|
||||
},
|
||||
handleSelectGoods(item) {
|
||||
// 调起选择商品
|
||||
this.$refs.liliDialog.open("goods", "single");
|
||||
},
|
||||
// 选择链接回调
|
||||
selectedLink(val) {
|
||||
this.selected.url = this.$filters.formatLinkType(val);
|
||||
this.selected.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
},
|
||||
// 选择商品回调
|
||||
selectedGoodsData(val) {
|
||||
let goods = val[0];
|
||||
this.selected.img = goods.thumbnail;
|
||||
this.selected.price = goods.price;
|
||||
this.selected.name = goods.goodsName;
|
||||
this.selected.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
},
|
||||
handleSelectImg() {
|
||||
// 选择图片
|
||||
this.picModelFlag = true;
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.ossManage) {
|
||||
this.$refs.ossManage.selectImage = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
// 选择图片回调
|
||||
callbackSelected(val) {
|
||||
this.picModelFlag = false;
|
||||
if (!val?.url) return;
|
||||
this.selected.img = val.url;
|
||||
},
|
||||
handleCancelModal() {
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal = false;
|
||||
},
|
||||
handleConfirmModal() {
|
||||
if (this.editTarget) {
|
||||
Object.assign(this.editTarget, this.cloneItem(this.selected));
|
||||
}
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal = false;
|
||||
},
|
||||
handleCancelModal1() {
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal1 = false;
|
||||
},
|
||||
handleConfirmModal1() {
|
||||
if (this.editTarget) {
|
||||
Object.assign(this.editTarget, this.cloneItem(this.selected));
|
||||
}
|
||||
this.editTarget = null;
|
||||
this.selected = {};
|
||||
this.showModal1 = false;
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const msgLeft = ref(props.data.options.contentLeft); // 左侧数据
|
||||
const msgRight = ref(props.data.options.contentRight); // 右侧数据
|
||||
const showModal = ref(false); // modal显隐
|
||||
const showModal1 = ref(false); // modal显隐
|
||||
const selected = ref({}); // 弹窗编辑草稿
|
||||
const editTarget = ref(null); // 确认后写回的原数据
|
||||
const picModelFlag = ref(false); // 图片选择
|
||||
|
||||
// ossManageRef:模板 ref 与组件 import 同名,改名避免互相覆盖
|
||||
const liliDialog = ref(null);
|
||||
const ossManageRef = ref(null);
|
||||
|
||||
function cloneItem(item) {
|
||||
return JSON.parse(JSON.stringify(item || {}));
|
||||
}
|
||||
// 编辑
|
||||
function handleSelectModel(item, type) {
|
||||
editTarget.value = item;
|
||||
selected.value = cloneItem(item);
|
||||
if (type) {
|
||||
showModal1.value = true;
|
||||
} else {
|
||||
showModal.value = true;
|
||||
}
|
||||
}
|
||||
function handleSelectLink(item, index) {
|
||||
// 调起选择链接弹窗
|
||||
liliDialog.value.open("link");
|
||||
}
|
||||
function handleSelectGoods(item) {
|
||||
// 调起选择商品
|
||||
liliDialog.value.open("goods", "single");
|
||||
}
|
||||
// 选择链接回调
|
||||
function selectedLink(val) {
|
||||
selected.value.url = formatLinkType(val);
|
||||
selected.value.type =
|
||||
val.___type === "other" && val.url === "" ? "link" : "other";
|
||||
}
|
||||
// 选择商品回调
|
||||
function selectedGoodsData(val) {
|
||||
let goods = val[0];
|
||||
selected.value.img = goods.thumbnail;
|
||||
selected.value.price = goods.price;
|
||||
selected.value.name = goods.goodsName;
|
||||
selected.value.url = `/goodsDetail?skuId=${goods.id}&goodsId=${goods.goodsId}`;
|
||||
}
|
||||
function handleSelectImg() {
|
||||
// 选择图片
|
||||
picModelFlag.value = true;
|
||||
nextTick(() => {
|
||||
if (ossManageRef.value) {
|
||||
ossManageRef.value.selectImage = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
// 选择图片回调
|
||||
function callbackSelected(val) {
|
||||
picModelFlag.value = false;
|
||||
if (!val?.url) return;
|
||||
selected.value.img = val.url;
|
||||
}
|
||||
function handleCancelModal() {
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleConfirmModal() {
|
||||
if (editTarget.value) {
|
||||
Object.assign(editTarget.value, cloneItem(selected.value));
|
||||
}
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal.value = false;
|
||||
}
|
||||
function handleCancelModal1() {
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal1.value = false;
|
||||
}
|
||||
function handleConfirmModal1() {
|
||||
if (editTarget.value) {
|
||||
Object.assign(editTarget.value, cloneItem(selected.value));
|
||||
}
|
||||
editTarget.value = null;
|
||||
selected.value = {};
|
||||
showModal1.value = false;
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import "./setup-box.scss";
|
||||
|
||||
@@ -35,103 +35,100 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: this.data.options, // 当前数据
|
||||
actStatus: 0, // 0 未开始 1 进行中 2 已结束
|
||||
actName: "限时秒杀",
|
||||
currHour: "00", // 当前秒杀场
|
||||
diffSeconds: 0, // 倒地时
|
||||
hours: "00", // 小时
|
||||
minutes: "00", // 分钟
|
||||
seconds: "00", // 秒
|
||||
interval: undefined, // 定时器
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
diffSeconds(val) {
|
||||
const hours = Math.floor(val / 3600);
|
||||
// 当前秒数 / 60,向下取整
|
||||
// 获取到所有分钟数 3600 / 60 = 60分钟
|
||||
// 对60取模,超过小时数的分钟数
|
||||
const minutes = Math.floor(val / 60) % 60;
|
||||
// 当前的秒数 % 60,获取到 超过小时数、分钟数的秒数(秒数)
|
||||
const seconds = val % 60;
|
||||
this.hours = hours < 10 ? "0" + hours : hours;
|
||||
this.minutes = minutes < 10 ? "0" + minutes : minutes;
|
||||
this.seconds = seconds < 10 ? "0" + seconds : seconds;
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, onBeforeUnmount } from "vue";
|
||||
|
||||
if (val === 0) {
|
||||
clearInterval(this.interval);
|
||||
this.hours = 0;
|
||||
this.minutes = 0;
|
||||
this.seconds = 0;
|
||||
this.countDown(this.options.list);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const options = ref(props.data.options); // 当前数据
|
||||
const actStatus = ref(0); // 0 未开始 1 进行中 2 已结束
|
||||
const actName = ref("限时秒杀");
|
||||
const currHour = ref("00"); // 当前秒杀场
|
||||
const diffSeconds = ref(0); // 倒地时
|
||||
const hours = ref("00"); // 小时
|
||||
const minutes = ref("00"); // 分钟
|
||||
const seconds = ref("00"); // 秒
|
||||
const interval = ref(undefined); // 定时器
|
||||
|
||||
watch(diffSeconds, (val) => {
|
||||
const hours = Math.floor(val / 3600);
|
||||
// 当前秒数 / 60,向下取整
|
||||
// 获取到所有分钟数 3600 / 60 = 60分钟
|
||||
// 对60取模,超过小时数的分钟数
|
||||
const minutes = Math.floor(val / 60) % 60;
|
||||
// 当前的秒数 % 60,获取到 超过小时数、分钟数的秒数(秒数)
|
||||
const seconds = val % 60;
|
||||
hours.value = hours < 10 ? "0" + hours : hours;
|
||||
minutes.value = minutes < 10 ? "0" + minutes : minutes;
|
||||
seconds.value = seconds < 10 ? "0" + seconds : seconds;
|
||||
|
||||
if (val === 0) {
|
||||
clearInterval(interval.value);
|
||||
hours.value = 0;
|
||||
minutes.value = 0;
|
||||
seconds.value = 0;
|
||||
countDown(options.value.list);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
countDown(options.value.list);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(interval.value);
|
||||
});
|
||||
|
||||
// 倒计时
|
||||
function countDown(list) {
|
||||
/**
|
||||
* 默认倒计时两小时
|
||||
* 如果没有开始,则显示未开始
|
||||
* 进行中显示倒计时
|
||||
* 今天的秒杀结束则显示已结束
|
||||
*/
|
||||
let nowHour = new Date().getHours();
|
||||
if (nowHour < Number(list[0].time)) {
|
||||
// 活动未开始
|
||||
currHour.value = list[0].time;
|
||||
actStatus.value = 0;
|
||||
} else if (nowHour >= Number(list[list.length - 1].time + 2)) {
|
||||
// 活动已结束
|
||||
actStatus.value = 2;
|
||||
currHour.value = list[list.length - 1].time;
|
||||
} else {
|
||||
// 活动进行中
|
||||
actStatus.value = 1;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (nowHour == Number(list[i].time)) {
|
||||
currHour.value = list[i].time;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.countDown(this.options.list);
|
||||
},
|
||||
beforeUnmount() {
|
||||
clearInterval(this.interval);
|
||||
},
|
||||
methods: {
|
||||
// 倒计时
|
||||
countDown(list) {
|
||||
/**
|
||||
* 默认倒计时两小时
|
||||
* 如果没有开始,则显示未开始
|
||||
* 进行中显示倒计时
|
||||
* 今天的秒杀结束则显示已结束
|
||||
*/
|
||||
let nowHour = new Date().getHours();
|
||||
if (nowHour < Number(list[0].time)) {
|
||||
// 活动未开始
|
||||
this.currHour = list[0].time;
|
||||
this.actStatus = 0;
|
||||
} else if (nowHour >= Number(list[list.length - 1].time + 2)) {
|
||||
// 活动已结束
|
||||
this.actStatus = 2;
|
||||
this.currHour = list[list.length - 1].time;
|
||||
} else {
|
||||
// 活动进行中
|
||||
this.actStatus = 1;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (nowHour == Number(list[i].time)) {
|
||||
this.currHour = list[i].time;
|
||||
}
|
||||
if (
|
||||
nowHour > Number(list[i].time) &&
|
||||
nowHour < Number(list[i].time + 2)
|
||||
) {
|
||||
this.currHour = list[i].time;
|
||||
}
|
||||
}
|
||||
// 当前0点时间戳
|
||||
let zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
||||
// 活动倒计时
|
||||
this.diffSeconds = Math.floor(
|
||||
(zeroTime +
|
||||
3600 * 1000 * (this.currHour + 2) -
|
||||
new Date().getTime()) /
|
||||
1000
|
||||
);
|
||||
this.interval = setInterval(() => {
|
||||
this.diffSeconds--;
|
||||
}, 1000);
|
||||
if (
|
||||
nowHour > Number(list[i].time) &&
|
||||
nowHour < Number(list[i].time + 2)
|
||||
) {
|
||||
currHour.value = list[i].time;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
// 当前0点时间戳
|
||||
let zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
||||
// 活动倒计时
|
||||
diffSeconds.value = Math.floor(
|
||||
(zeroTime +
|
||||
3600 * 1000 * (currHour.value + 2) -
|
||||
new Date().getTime()) /
|
||||
1000
|
||||
);
|
||||
interval.value = setInterval(() => {
|
||||
diffSeconds.value--;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.seckill {
|
||||
|
||||
@@ -35,103 +35,100 @@
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: this.data.options, // 当前数据
|
||||
actStatus: 0, // 0 未开始 1 进行中 2 已结束
|
||||
actName: "限时秒杀",
|
||||
currHour: "00", // 当前秒杀场
|
||||
diffSeconds: 0, // 倒地时
|
||||
hours: "00", // 小时
|
||||
minutes: "00", // 分钟
|
||||
seconds: "00", // 秒
|
||||
interval: undefined, // 定时器
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
diffSeconds(val) {
|
||||
const hours = Math.floor(val / 3600);
|
||||
// 当前秒数 / 60,向下取整
|
||||
// 获取到所有分钟数 3600 / 60 = 60分钟
|
||||
// 对60取模,超过小时数的分钟数
|
||||
const minutes = Math.floor(val / 60) % 60;
|
||||
// 当前的秒数 % 60,获取到 超过小时数、分钟数的秒数(秒数)
|
||||
const seconds = val % 60;
|
||||
this.hours = hours < 10 ? "0" + hours : hours;
|
||||
this.minutes = minutes < 10 ? "0" + minutes : minutes;
|
||||
this.seconds = seconds < 10 ? "0" + seconds : seconds;
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, onBeforeUnmount } from "vue";
|
||||
|
||||
if (val === 0) {
|
||||
clearInterval(this.interval);
|
||||
this.hours = 0;
|
||||
this.minutes = 0;
|
||||
this.seconds = 0;
|
||||
this.countDown(this.options.list);
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
});
|
||||
|
||||
const options = ref(props.data.options); // 当前数据
|
||||
const actStatus = ref(0); // 0 未开始 1 进行中 2 已结束
|
||||
const actName = ref("限时秒杀");
|
||||
const currHour = ref("00"); // 当前秒杀场
|
||||
const diffSeconds = ref(0); // 倒地时
|
||||
const hours = ref("00"); // 小时
|
||||
const minutes = ref("00"); // 分钟
|
||||
const seconds = ref("00"); // 秒
|
||||
const interval = ref(undefined); // 定时器
|
||||
|
||||
watch(diffSeconds, (val) => {
|
||||
const h = Math.floor(val / 3600);
|
||||
// 当前秒数 / 60,向下取整
|
||||
// 获取到所有分钟数 3600 / 60 = 60分钟
|
||||
// 对60取模,超过小时数的分钟数
|
||||
const m = Math.floor(val / 60) % 60;
|
||||
// 当前的秒数 % 60,获取到 超过小时数、分钟数的秒数(秒数)
|
||||
const s = val % 60;
|
||||
hours.value = h < 10 ? "0" + h : h;
|
||||
minutes.value = m < 10 ? "0" + m : m;
|
||||
seconds.value = s < 10 ? "0" + s : s;
|
||||
|
||||
if (val === 0) {
|
||||
clearInterval(interval.value);
|
||||
hours.value = 0;
|
||||
minutes.value = 0;
|
||||
seconds.value = 0;
|
||||
countDown(options.value.list);
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
countDown(options.value.list);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(interval.value);
|
||||
});
|
||||
|
||||
// 倒计时
|
||||
function countDown(list) {
|
||||
/**
|
||||
* 默认倒计时两小时
|
||||
* 如果没有开始,则显示未开始
|
||||
* 进行中显示倒计时
|
||||
* 今天的秒杀结束则显示已结束
|
||||
*/
|
||||
let nowHour = new Date().getHours();
|
||||
if (nowHour < Number(list[0].time)) {
|
||||
// 活动未开始
|
||||
currHour.value = list[0].time;
|
||||
actStatus.value = 0;
|
||||
} else if (nowHour >= Number(list[list.length - 1].time + 2)) {
|
||||
// 活动已结束
|
||||
actStatus.value = 2;
|
||||
currHour.value = list[list.length - 1].time;
|
||||
} else {
|
||||
// 活动进行中
|
||||
actStatus.value = 1;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (nowHour == Number(list[i].time)) {
|
||||
currHour.value = list[i].time;
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.countDown(this.options.list);
|
||||
},
|
||||
beforeUnmount() {
|
||||
clearInterval(this.interval);
|
||||
},
|
||||
methods: {
|
||||
// 倒计时
|
||||
countDown(list) {
|
||||
/**
|
||||
* 默认倒计时两小时
|
||||
* 如果没有开始,则显示未开始
|
||||
* 进行中显示倒计时
|
||||
* 今天的秒杀结束则显示已结束
|
||||
*/
|
||||
let nowHour = new Date().getHours();
|
||||
if (nowHour < Number(list[0].time)) {
|
||||
// 活动未开始
|
||||
this.currHour = list[0].time;
|
||||
this.actStatus = 0;
|
||||
} else if (nowHour >= Number(list[list.length - 1].time + 2)) {
|
||||
// 活动已结束
|
||||
this.actStatus = 2;
|
||||
this.currHour = list[list.length - 1].time;
|
||||
} else {
|
||||
// 活动进行中
|
||||
this.actStatus = 1;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (nowHour == Number(list[i].time)) {
|
||||
this.currHour = list[i].time;
|
||||
}
|
||||
if (
|
||||
nowHour > Number(list[i].time) &&
|
||||
nowHour < Number(list[i].time + 2)
|
||||
) {
|
||||
this.currHour = list[i].time;
|
||||
}
|
||||
}
|
||||
// 当前0点时间戳
|
||||
let zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
||||
// 活动倒计时
|
||||
this.diffSeconds = Math.floor(
|
||||
(zeroTime +
|
||||
3600 * 1000 * (this.currHour + 2) -
|
||||
new Date().getTime()) /
|
||||
1000
|
||||
);
|
||||
this.interval = setInterval(() => {
|
||||
this.diffSeconds--;
|
||||
}, 1000);
|
||||
if (
|
||||
nowHour > Number(list[i].time) &&
|
||||
nowHour < Number(list[i].time + 2)
|
||||
) {
|
||||
currHour.value = list[i].time;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
// 当前0点时间戳
|
||||
let zeroTime = new Date(new Date().toLocaleDateString()).getTime();
|
||||
// 活动倒计时
|
||||
diffSeconds.value = Math.floor(
|
||||
(zeroTime +
|
||||
3600 * 1000 * (currHour.value + 2) -
|
||||
new Date().getTime()) /
|
||||
1000
|
||||
);
|
||||
interval.value = setInterval(() => {
|
||||
diffSeconds.value--;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.seckill {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user