diff --git a/manager/src/views/sys/message/messageTemplate.vue b/manager/src/views/sys/message/messageTemplate.vue
index 0ba020517..092ab08ed 100644
--- a/manager/src/views/sys/message/messageTemplate.vue
+++ b/manager/src/views/sys/message/messageTemplate.vue
@@ -2,217 +2,276 @@
-
-
-
-
-
-
-
-
- toggleNoticeChannel(v, row)"
- />
-
-
-
-
- toggleOaChannel(v, row)"
- />
-
-
-
-
- toggleMpChannel(v, row)"
- />
-
-
-
-
- toggleEmailChannel(v, row)"
- />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ handleSwitchChange(row, 'NOTICE', v)"
+ />
+
+
+
+
+
+
+ handleSwitchChange(row, 'SMS', v)"
+ />
+
+
+
+
+
+
+ handleSwitchChange(row, 'WECHAT_OA', v)"
+ />
+
+
+
+
+
+
+ handleSwitchChange(row, 'WECHAT_MP', v)"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
公众号通知设置
+
+
+
+ 关闭后小程序端不展示公众号通知入口
+
+
+
+
+
+
+
+ 预览
+
+
+
+ 服务器配置(公众号后台 → 设置与开发 → 基本配置)
+
+
+
+
+
+
+
+
+
+
+ 保存设置
+
+
+
+
+
+
微信模板同步
+
+ 同步服务号模板
+ 同步微信公众号(服务号)-模板消息,行业类目:电商平台
+
+
+ 同步小程序模板
+ 同步微信小程序订阅消息模板,行业类目:电商平台
+
+
+
-
-
-
-
+
+
+
+
+
-
- toggleNoticeChannel(v, currentRow)"
+
+
+
+
+
-
-
-
- 关闭
+ 取消
+ 确定保存
-
-
-
-
+
+
+
+
+
-
- toggleOaChannel(v, currentRow)"
+
+
+
+
+
-
-
-
-
-
-
-
- 关闭
+ 取消
+ 确定保存
-
+
+
-
-
+
+
-
- toggleEmailChannel(v, currentRow)"
+
+
+
+
+
-
-
-
- 关闭
+ 取消
+ 确定保存
-
-
-
-
+
+
+
+
+
-
- toggleMpChannel(v, currentRow)"
- />
+
+
-
-
-
-
-
-
-
-
-
-
+
-
- 关闭
+ 取消
+ 确定保存
@@ -222,40 +281,105 @@
import { onMounted, ref } from "vue";
import { ElMessage } from "element-plus";
import * as API_Setting from "@/api/setting.js";
+import { getSetting, setSetting } from "@/api/index";
defineOptions({ name: "messageTemplate" });
const loading = ref(false);
-const activeTab = ref("member");
+const saving = ref(false);
+const activeTab = ref("MEMBER");
const syncOaLoading = ref(false);
const syncMpLoading = ref(false);
const total = ref(0);
+
const searchForm = ref({
pageNumber: 1,
pageSize: 20,
});
-const memberRows = ref([]);
-const currentRow = ref({});
-const noticeDetailText = ref("");
+
+const tableRows = ref([]);
+
+// 弹窗状态与表单对象
const noticeModalVisible = ref(false);
+const formNotice = ref({ id: "", sceneName: "", title: "", content: "", status: "CLOSE" });
+
+const smsModalVisible = ref(false);
+const formSms = ref({ id: "", sceneName: "", smsTemplateCode: "", content: "", status: "CLOSE" });
+
const oaModalVisible = ref(false);
+const formOa = ref({ id: "", sceneName: "", code: "", content: "", enable: false });
+
const mpModalVisible = ref(false);
-const emailModalVisible = ref(false);
-const emailDetailText = ref("");
+const formMp = ref({ id: "", sceneName: "", code: "", content: "", enable: false });
+
+// 公众号通知设置(SettingEnum.WECHAT_OFFICIAL_ACCOUNT_SETTING)
+const OA_SETTING_KEY = "WECHAT_OFFICIAL_ACCOUNT_SETTING";
+const oaSettingLoading = ref(false);
+const oaSettingSaving = ref(false);
+const emptyOaSetting = () => ({
+ enabled: false,
+ accountName: "",
+ articleUrl: "",
+ serverToken: "",
+ encodingAesKey: "",
+ subscribeReply: "",
+});
+const oaSettingForm = ref(emptyOaSetting());
+
+async function loadOaSetting() {
+ oaSettingLoading.value = true;
+ try {
+ const res = await getSetting(OA_SETTING_KEY);
+ const data = (res && res.success && res.result) || {};
+ oaSettingForm.value = Object.assign(emptyOaSetting(), data, { enabled: data.enabled === true });
+ } finally {
+ oaSettingLoading.value = false;
+ }
+}
+
+function previewOaArticle() {
+ if (oaSettingForm.value.articleUrl) window.open(oaSettingForm.value.articleUrl, "_blank");
+}
+
+async function saveOaSetting() {
+ const form = oaSettingForm.value;
+ const url = (form.articleUrl || "").trim();
+ if (form.enabled && !url) {
+ ElMessage.warning("开启公众号通知时必须配置引导关注文章链接");
+ return;
+ }
+ if (url && !url.startsWith("https://mp.weixin.qq.com/")) {
+ ElMessage.warning("文章链接必须是 https://mp.weixin.qq.com/ 开头的公众号文章地址");
+ return;
+ }
+ const aesKey = (form.encodingAesKey || "").trim();
+ if (aesKey && aesKey.length !== 43) {
+ ElMessage.warning("EncodingAESKey 长度必须为 43 位");
+ return;
+ }
+ oaSettingSaving.value = true;
+ try {
+ const res = await setSetting(OA_SETTING_KEY, { ...form, articleUrl: url, encodingAesKey: aesKey });
+ if (res && res.success) {
+ ElMessage.success("保存成功");
+ }
+ } finally {
+ oaSettingSaving.value = false;
+ }
+}
function isOpen(status) {
return String(status || "").toUpperCase() === "OPEN";
}
+
function wechatEnableToOpenClose(enable) {
- if (enable === true || enable === "true" || enable === 1) return "OPEN";
- return "CLOSE";
-}
-function toOpenClose(flag) {
- return flag ? "OPEN" : "CLOSE";
+ return enable === true || enable === "true" || enable === 1 ? "OPEN" : "CLOSE";
}
+
function safeText(v) {
return v === undefined || v === null ? "" : String(v);
}
+
function pickByKeys(obj, keys) {
for (let i = 0; i < keys.length; i++) {
const val = obj ? obj[keys[i]] : "";
@@ -263,6 +387,7 @@ function pickByKeys(obj, keys) {
}
return "";
}
+
function formatOaContent(item) {
if (!item) return "";
const first = pickByKeys(item, ["first", "title", "name"]);
@@ -272,8 +397,56 @@ function formatOaContent(item) {
: safeText(item.keywordsText || "");
return [first, keywords, remark].filter(Boolean).join("\n");
}
+
+const MP_KEYWORD_LABELS = {
+ GOODS_INFO: "物品名称",
+ ORDER_SN: "订单编号",
+ AFTER_SALE_SN: "售后单号",
+ AUDIT_RESULT: "审核结果",
+ CHANGE_POINT: "变动数量",
+ CURRENT_POINT: "当前积分",
+ POINT_TIME: "积分变更时间",
+ CHANGE_AMOUNT: "变动金额",
+ WALLET_BALANCE: "账户余额",
+ WALLET_TIME: "时间",
+ CHANGE_REASON: "变动原因",
+ WITHDRAW_AMOUNT: "提现金额",
+ WITHDRAW_RESULT: "提现结果",
+ APPLY_TIME: "申请时间",
+ WARM_REMIND: "温馨提醒",
+ LOGISTICS_TIME: "发货时间",
+ COMPLETE_TIME: "完成时间",
+ PINTUAN_TIME: "成团时间",
+ REMARK: "温馨提示",
+ LOGISTICS_NAME: "快递公司",
+ LOGISTICS_NO: "快递单号",
+ SHOP_NAME: "商家名称",
+ MEMBER_NAME: "买家昵称",
+ PRICE: "订单金额",
+ PAYMENT_TIME: "支付时间",
+};
+
function formatMpContent(item) {
if (!item) return "";
+ try {
+ const keys = typeof item.keywords === "string" ? JSON.parse(item.keywords) : item.keywords;
+ const texts = typeof item.keywordsText === "string" ? JSON.parse(item.keywordsText) : item.keywordsText;
+ if (Array.isArray(keys) && Array.isArray(texts) && keys.length === texts.length && keys.length > 0) {
+ const isGoodsNameLabel = item.sceneCode !== "ORDER_DELIVER";
+ return keys
+ .map((k, idx) => {
+ let label = MP_KEYWORD_LABELS[k] || k;
+ if (isGoodsNameLabel && k === "GOODS_INFO") label = "商品名称";
+ if (isGoodsNameLabel && k === "ORDER_SN") label = "订单号";
+ const field = texts[idx] || "";
+ const placeholder = field.includes(".DATA") ? `{{${field}}}` : `{{${field}.DATA}}`;
+ return `${label}:${placeholder}`;
+ })
+ .join("\n");
+ }
+ } catch (e) {
+ // fallback below
+ }
const keywordsText = safeText(item.keywordsText);
if (keywordsText) return keywordsText;
if (Array.isArray(item.keywords)) {
@@ -281,55 +454,234 @@ function formatMpContent(item) {
}
return safeText(item.content);
}
-async function loadMemberPage() {
+
+// 加载分页
+async function loadPage() {
loading.value = true;
try {
- const res = await API_Setting.getMessageTemplateAggregatePage(searchForm.value);
+ const params = {
+ ...searchForm.value,
+ type: activeTab.value,
+ };
+ const res = await API_Setting.getMessageTemplateAggregatePage(params);
if (!(res && res.success && res.result)) {
- memberRows.value = [];
+ tableRows.value = [];
total.value = 0;
ElMessage.error((res && res.message) || "加载消息模板失败");
return;
}
const records = res.result.records || [];
total.value = res.result.total || 0;
- memberRows.value = records.map((agg, idx) => {
+ tableRows.value = records.map((agg, idx) => {
const item = { ...(agg.notice || {}) };
const oa = agg.wechatOa;
const mp = agg.wechatMp;
return {
...item,
- emailStatus: item.emailStatus || "CLOSE",
- emailContent: item.emailContent || "",
+ smsStatus: item.smsStatus || "CLOSE",
+ smsTemplateCode: item.smsTemplateCode || "",
+ smsContent: item.smsContent || "",
index: (searchForm.value.pageNumber - 1) * searchForm.value.pageSize + idx + 1,
oaId: oa ? oa.id : "",
oaName: oa ? pickByKeys(oa, ["name", "templateName", "title"]) : "",
oaCode: oa ? pickByKeys(oa, ["code", "templateId"]) : "",
- oaStatus: oa ? wechatEnableToOpenClose(oa.enable) : "",
+ oaStatus: oa ? wechatEnableToOpenClose(oa.enable) : "CLOSE",
oaRaw: oa || null,
oaContent: formatOaContent(oa),
mpId: mp ? mp.id : "",
mpName: mp ? pickByKeys(mp, ["name", "templateName", "title"]) : "",
- mpTemplateId: mp ? pickByKeys(mp, ["templateId"]) : "",
- mpCode: mp ? pickByKeys(mp, ["code"]) : "",
- mpStatus: mp ? wechatEnableToOpenClose(mp.enable) : "",
+ mpCode: mp ? pickByKeys(mp, ["code", "templateId"]) : "",
+ mpStatus: mp ? wechatEnableToOpenClose(mp.enable) : "CLOSE",
mpRaw: mp || null,
mpContent: formatMpContent(mp),
+ _switchLoading: "",
};
});
} finally {
loading.value = false;
}
}
-function reloadCurrent() {
- if (activeTab.value === "member") loadMemberPage();
+
+function handleTabChange() {
+ searchForm.value.pageNumber = 1;
+ if (activeTab.value === "SETTING") {
+ loadOaSetting();
+ return;
+ }
+ loadPage();
}
+
+function changePageSize() {
+ searchForm.value.pageNumber = 1;
+ loadPage();
+}
+
+// 行内 Switch 快速切换
+async function handleSwitchChange(row, channel, value) {
+ const targetStatus = value ? "OPEN" : "CLOSE";
+ row._switchLoading = channel;
+ try {
+ const res = await API_Setting.updateMessageChannelSwitch({
+ id: row.id,
+ channel,
+ status: targetStatus,
+ });
+ if (res && res.success) {
+ if (channel === "NOTICE") row.noticeStatus = targetStatus;
+ else if (channel === "SMS") row.smsStatus = targetStatus;
+ else if (channel === "WECHAT_OA") row.oaStatus = targetStatus;
+ else if (channel === "WECHAT_MP") row.mpStatus = targetStatus;
+ ElMessage.success("开关状态已更新");
+ } else {
+ ElMessage.error((res && res.message) || "更新开关失败");
+ }
+ } catch (e) {
+ ElMessage.error("网络异常,更新开关失败");
+ } finally {
+ row._switchLoading = "";
+ }
+}
+
+// 1. 打开站内信弹窗
+function openNoticeModal(row) {
+ formNotice.value = {
+ id: row.id,
+ sceneName: row.noticeNode,
+ title: row.noticeTitle || "",
+ content: row.noticeContent || "",
+ status: isOpen(row.noticeStatus) ? "OPEN" : "CLOSE",
+ };
+ noticeModalVisible.value = true;
+}
+
+async function saveNoticeModal() {
+ if (!formNotice.value.content) {
+ ElMessage.warning("请输入模板内容");
+ return;
+ }
+ saving.value = true;
+ try {
+ const res = await API_Setting.updateNoticeMessageTemplate({
+ id: formNotice.value.id,
+ title: formNotice.value.title,
+ content: formNotice.value.content,
+ status: formNotice.value.status,
+ });
+ if (res && res.success) {
+ ElMessage.success("站内信配置已保存");
+ noticeModalVisible.value = false;
+ loadPage();
+ } else {
+ ElMessage.error((res && res.message) || "保存失败");
+ }
+ } finally {
+ saving.value = false;
+ }
+}
+
+// 2. 打开短信弹窗
+function openSmsModal(row) {
+ formSms.value = {
+ id: row.id,
+ sceneName: row.noticeNode,
+ smsTemplateCode: row.smsTemplateCode || "",
+ content: row.smsContent || "",
+ status: isOpen(row.smsStatus) ? "OPEN" : "CLOSE",
+ };
+ smsModalVisible.value = true;
+}
+
+async function saveSmsModal() {
+ saving.value = true;
+ try {
+ const res = await API_Setting.updateSmsMessageTemplateConfig({
+ id: formSms.value.id,
+ smsTemplateCode: formSms.value.smsTemplateCode,
+ status: formSms.value.status,
+ });
+ if (res && res.success) {
+ ElMessage.success("短信模板配置已保存");
+ smsModalVisible.value = false;
+ loadPage();
+ } else {
+ ElMessage.error((res && res.message) || "保存失败");
+ }
+ } finally {
+ saving.value = false;
+ }
+}
+
+// 3. 打开微信公众号弹窗
+function openOaModal(row) {
+ formOa.value = {
+ id: row.id,
+ sceneName: row.noticeNode,
+ code: row.oaCode || "",
+ content: row.oaContent || "",
+ enable: isOpen(row.oaStatus),
+ };
+ oaModalVisible.value = true;
+}
+
+async function saveOaModal() {
+ saving.value = true;
+ try {
+ const res = await API_Setting.updateWechatOaMessageTemplate({
+ id: formOa.value.id,
+ code: formOa.value.code,
+ enable: formOa.value.enable,
+ });
+ if (res && res.success) {
+ ElMessage.success("微信公众号模板配置已保存");
+ oaModalVisible.value = false;
+ loadPage();
+ } else {
+ ElMessage.error((res && res.message) || "保存失败");
+ }
+ } finally {
+ saving.value = false;
+ }
+}
+
+// 4. 打开微信小程序弹窗
+function openMpModal(row) {
+ formMp.value = {
+ id: row.id,
+ sceneName: row.noticeNode,
+ code: row.mpCode || "",
+ content: row.mpContent || "",
+ enable: isOpen(row.mpStatus),
+ };
+ mpModalVisible.value = true;
+}
+
+async function saveMpModal() {
+ saving.value = true;
+ try {
+ const res = await API_Setting.updateWechatMpMessageTemplate({
+ id: formMp.value.id,
+ code: formMp.value.code,
+ enable: formMp.value.enable,
+ });
+ if (res && res.success) {
+ ElMessage.success("微信小程序订阅消息配置已保存");
+ mpModalVisible.value = false;
+ loadPage();
+ } else {
+ ElMessage.error((res && res.message) || "保存失败");
+ }
+ } finally {
+ saving.value = false;
+ }
+}
+
+// 同步微信服务号模板
async function syncOaTemplates() {
syncOaLoading.value = true;
try {
const res = await API_Setting.wechatMessageSync();
if (res && res.success) {
- await loadMemberPage();
+ await loadPage();
ElMessage.success("服务号模板同步成功");
} else {
ElMessage.error((res && res.message) || "服务号模板同步失败");
@@ -338,12 +690,14 @@ async function syncOaTemplates() {
syncOaLoading.value = false;
}
}
+
+// 同步微信小程序模板
async function syncMpTemplates() {
syncMpLoading.value = true;
try {
const res = await API_Setting.wechatMPMessageSync();
if (res && res.success) {
- await loadMemberPage();
+ await loadPage();
ElMessage.success("小程序模板同步成功");
} else {
ElMessage.error((res && res.message) || "小程序模板同步失败");
@@ -352,130 +706,96 @@ async function syncMpTemplates() {
syncMpLoading.value = false;
}
}
-function changePageSize() {
- searchForm.value.pageNumber = 1;
- loadMemberPage();
-}
-async function openNoticeModal(row) {
- currentRow.value = { ...row };
- noticeDetailText.value = row.noticeContent || "";
- noticeModalVisible.value = true;
- const res = await API_Setting.getNoticeMessageDetail(row.id);
- if (res && res.success && res.result) {
- const d = res.result;
- noticeDetailText.value = [d.noticeTitle, d.noticeContent].filter(Boolean).join("\n");
- }
-}
-function openOaModal(row) {
- currentRow.value = { ...row };
- oaModalVisible.value = true;
-}
-function openMpModal(row) {
- currentRow.value = { ...row };
- mpModalVisible.value = true;
-}
-async function openEmailModal(row) {
- currentRow.value = { ...row };
- const fallback = row.emailContent || row.noticeContent || "";
- emailDetailText.value = row.noticeTitle ? [row.noticeTitle, fallback].filter(Boolean).join("\n") : fallback;
- emailModalVisible.value = true;
- const res = await API_Setting.getNoticeMessageDetail(row.id);
- if (res && res.success && res.result) {
- const d = res.result;
- if (d.emailContent) {
- emailDetailText.value = d.emailContent;
- } else {
- emailDetailText.value = [d.noticeTitle, d.noticeContent].filter(Boolean).join("\n");
- }
- }
-}
-function updateRowStatus(row, key, val) {
- const idx = memberRows.value.findIndex((x) => x.id === row.id);
- if (idx >= 0) memberRows.value[idx][key] = val;
- if (currentRow.value && currentRow.value.id === row.id) currentRow.value[key] = val;
-}
-async function toggleNoticeChannel(v, row) {
- const old = row.noticeStatus;
- updateRowStatus(row, "noticeStatus", toOpenClose(v));
- const res = await API_Setting.updateMessageStatus(row.id, toOpenClose(v));
- if (!(res && res.success)) {
- updateRowStatus(row, "noticeStatus", old);
- ElMessage.error((res && res.message) || "站内信开关更新失败");
- } else {
- ElMessage.success("站内信开关更新成功");
- }
-}
-async function toggleOaChannel(v, row) {
- if (!row.oaId) return;
- const old = row.oaStatus;
- updateRowStatus(row, "oaStatus", toOpenClose(v));
- const params = { id: row.oaId, enable: v };
- try {
- const res = await API_Setting.editWechatMessageTemplate(row.oaId, params);
- if (!(res && res.success)) {
- updateRowStatus(row, "oaStatus", old);
- ElMessage.error((res && res.message) || "服务号开关更新失败");
- } else {
- ElMessage.success("服务号开关更新成功");
- }
- } catch (e) {
- updateRowStatus(row, "oaStatus", old);
- ElMessage.error((e && e.message) || "服务号开关更新失败");
- }
-}
-async function toggleMpChannel(v, row) {
- if (!row.mpId) return;
- const old = row.mpStatus;
- updateRowStatus(row, "mpStatus", toOpenClose(v));
- const params = { id: row.mpId, enable: v };
- const res = await API_Setting.editWechatMPMessageTemplate(row.mpId, params);
- if (!(res && res.success)) {
- updateRowStatus(row, "mpStatus", old);
- ElMessage.error((res && res.message) || "小程序开关更新失败");
- } else {
- ElMessage.success("小程序开关更新成功");
- }
-}
-async function toggleEmailChannel(v, row) {
- const old = row.emailStatus || "CLOSE";
- updateRowStatus(row, "emailStatus", toOpenClose(v));
- const res = await API_Setting.updateNoticeMessageEmailStatus(row.id, toOpenClose(v));
- if (!(res && res.success)) {
- updateRowStatus(row, "emailStatus", old);
- ElMessage.error((res && res.message) || "邮箱开关更新失败");
- } else {
- ElMessage.success("邮箱开关更新成功");
- }
-}
onMounted(() => {
- loadMemberPage();
+ loadPage();
});
-