258 lines
8.0 KiB
Vue
258 lines
8.0 KiB
Vue
<template>
|
||
<div class="aksrt-page-container">
|
||
<PageHeader title="微信模板消息" desc="配置微信公众号模板消息推送。" />
|
||
|
||
<!-- 状态卡片 -->
|
||
<div class="aksrt-wechat__status">
|
||
<div class="aksrt-wechat__status-card">
|
||
<div class="aksrt-wechat__status-icon" :class="status.configured ? 'aksrt-wechat__status-icon--ok' : 'aksrt-wechat__status-icon--warn'">
|
||
<i :class="status.configured ? 'fas fa-check' : 'fas fa-exclamation'"></i>
|
||
</div>
|
||
<div class="aksrt-wechat__status-info">
|
||
<div class="aksrt-wechat__status-label">配置状态</div>
|
||
<div class="aksrt-wechat__status-value">{{ status.configured ? '已配置' : '未配置' }}</div>
|
||
</div>
|
||
</div>
|
||
<div class="aksrt-wechat__status-card">
|
||
<div class="aksrt-wechat__status-icon" :class="status.has_token ? 'aksrt-wechat__status-icon--ok' : 'aksrt-wechat__status-icon--warn'">
|
||
<i :class="status.has_token ? 'fas fa-key' : 'fas fa-key'"></i>
|
||
</div>
|
||
<div class="aksrt-wechat__status-info">
|
||
<div class="aksrt-wechat__status-label">Access Token</div>
|
||
<div class="aksrt-wechat__status-value">{{ status.has_token ? '已获取' : '未获取' }}</div>
|
||
</div>
|
||
</div>
|
||
<div class="aksrt-wechat__status-card">
|
||
<div class="aksrt-wechat__status-icon" :class="status.has_template ? 'aksrt-wechat__status-icon--ok' : 'aksrt-wechat__status-icon--warn'">
|
||
<i :class="status.has_template ? 'fas fa-file-alt' : 'fas fa-file-alt'"></i>
|
||
</div>
|
||
<div class="aksrt-wechat__status-info">
|
||
<div class="aksrt-wechat__status-label">模板 ID</div>
|
||
<div class="aksrt-wechat__status-value">{{ status.has_template ? '已配置' : '未配置' }}</div>
|
||
</div>
|
||
</div>
|
||
<div class="aksrt-wechat__status-card">
|
||
<div class="aksrt-wechat__status-icon aksrt-wechat__status-icon--info">
|
||
<i class="fas fa-users"></i>
|
||
</div>
|
||
<div class="aksrt-wechat__status-info">
|
||
<div class="aksrt-wechat__status-label">绑定用户</div>
|
||
<div class="aksrt-wechat__status-value">{{ status.bound_users || 0 }} 人</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 配置表单 -->
|
||
<div class="aksrt-wechat__form-card">
|
||
<h3>基本配置</h3>
|
||
<div class="aksrt-form-group">
|
||
<label>
|
||
<input type="checkbox" v-model="form.enabled"> 启用微信模板消息
|
||
</label>
|
||
</div>
|
||
<div class="aksrt-form-group">
|
||
<label>AppID</label>
|
||
<input v-model="form.app_id" type="text" class="aksrt-input" placeholder="微信公众号 AppID">
|
||
</div>
|
||
<div class="aksrt-form-group">
|
||
<label>AppSecret</label>
|
||
<input v-model="form.app_secret" type="password" class="aksrt-input" placeholder="微信公众号 AppSecret">
|
||
<p class="aksrt-form-hint">保存后不会回显完整密钥</p>
|
||
</div>
|
||
<div class="aksrt-form-group">
|
||
<label>模板 ID</label>
|
||
<input v-model="form.template_id" type="text" class="aksrt-input" placeholder="微信模板消息模板 ID">
|
||
</div>
|
||
<div class="aksrt-form-actions">
|
||
<button class="aksrt-button aksrt-button--primary" :disabled="saving" @click="saveSettings">
|
||
{{ saving ? '保存中...' : '保存设置' }}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 通知类型 -->
|
||
<div class="aksrt-wechat__form-card">
|
||
<h3>支持的通知类型</h3>
|
||
<p class="aksrt-form-hint">以下事件类型可触发微信模板消息推送(需用户绑定微信且开启对应订阅):</p>
|
||
<table class="aksrt-table aksrt-table--sm">
|
||
<thead>
|
||
<tr>
|
||
<th>事件类型</th>
|
||
<th>说明</th>
|
||
<th>状态</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-for="evt in eventTypes" :key="evt.key">
|
||
<td><code>{{ evt.key }}</code></td>
|
||
<td>{{ evt.desc }}</td>
|
||
<td>
|
||
<span class="aksrt-badge" :class="evt.ready ? 'aksrt-badge--success' : 'aksrt-badge--warning'">
|
||
{{ evt.ready ? '可用' : '待接入' }}
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import { ref, reactive, computed, onMounted } from 'vue';
|
||
import PageHeader from '@/components/PageHeader.vue';
|
||
import api from '@/api';
|
||
|
||
export default {
|
||
name: 'UsersWechatTemplate',
|
||
components: { PageHeader },
|
||
setup() {
|
||
const status = ref({
|
||
enabled: false,
|
||
configured: false,
|
||
has_token: false,
|
||
has_template: false,
|
||
bound_users: 0,
|
||
});
|
||
|
||
const form = reactive({
|
||
enabled: false,
|
||
app_id: '',
|
||
app_secret: '',
|
||
template_id: '',
|
||
});
|
||
|
||
const saving = ref(false);
|
||
|
||
// Event type definitions — readiness driven by backend allowed_types
|
||
const eventTypeDefs = [
|
||
{ key: 'comment_reply', desc: '评论回复通知' },
|
||
{ key: 'system_notice', desc: '系统公告通知' },
|
||
{ key: 'order_paid', desc: '订单支付通知' },
|
||
{ key: 'credits_change', desc: '积分变动通知' },
|
||
];
|
||
|
||
const allowedTypes = ref([]);
|
||
|
||
const eventTypes = computed(() =>
|
||
eventTypeDefs.map(et => ({
|
||
...et,
|
||
ready: allowedTypes.value.length === 0 || allowedTypes.value.includes(et.key),
|
||
}))
|
||
);
|
||
|
||
async function loadStatus() {
|
||
try {
|
||
const json = await api.request('/wechat/status');
|
||
if (json.success) {
|
||
status.value = json.data;
|
||
form.enabled = json.data.enabled || false;
|
||
form.app_id = json.data.appid || '';
|
||
form.app_secret = json.data.app_secret === '***' ? '' : '';
|
||
form.template_id = json.data.template_id || '';
|
||
// Use backend allowed_types for readiness (empty = all allowed)
|
||
allowedTypes.value = json.data.allowed_types || [];
|
||
}
|
||
} catch (e) {
|
||
console.error('Load WeChat status failed:', e);
|
||
}
|
||
}
|
||
|
||
async function saveSettings() {
|
||
saving.value = true;
|
||
try {
|
||
const payload = { ...form };
|
||
// 如果密码为空且状态显示已配置,发送掩码保留原值
|
||
if (!payload.app_secret && status.value.configured) {
|
||
payload.app_secret = '***';
|
||
}
|
||
const json = await api.request('/wechat/settings', {
|
||
method: 'POST',
|
||
data: payload,
|
||
});
|
||
if (json.success) {
|
||
alert('设置已保存');
|
||
loadStatus();
|
||
} else {
|
||
alert(json.message || '保存失败');
|
||
}
|
||
} catch (e) {
|
||
console.error('Save failed:', e);
|
||
alert('保存失败: ' + (e.message || '未知错误'));
|
||
} finally {
|
||
saving.value = false;
|
||
}
|
||
}
|
||
|
||
onMounted(() => loadStatus());
|
||
|
||
return { status, form, saving, eventTypes, saveSettings };
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.aksrt-wechat__status {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 16px;
|
||
margin-bottom: 24px;
|
||
}
|
||
.aksrt-wechat__status-card {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 16px;
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
border: 1px solid #e8e8e8;
|
||
}
|
||
.aksrt-wechat__status-icon {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
}
|
||
.aksrt-wechat__status-icon--ok {
|
||
background: #d4edda;
|
||
color: #155724;
|
||
}
|
||
.aksrt-wechat__status-icon--warn {
|
||
background: #fff3cd;
|
||
color: #856404;
|
||
}
|
||
.aksrt-wechat__status-icon--info {
|
||
background: #d1ecf1;
|
||
color: #0c5460;
|
||
}
|
||
.aksrt-wechat__status-value {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1a1a2e;
|
||
}
|
||
.aksrt-wechat__status-label {
|
||
font-size: 12px;
|
||
color: #888;
|
||
}
|
||
.aksrt-wechat__form-card {
|
||
background: #fff;
|
||
border: 1px solid #e8e8e8;
|
||
border-radius: 8px;
|
||
padding: 24px;
|
||
margin-bottom: 16px;
|
||
}
|
||
.aksrt-wechat__form-card h3 {
|
||
margin: 0 0 16px;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
}
|
||
.aksrt-form-hint {
|
||
font-size: 12px;
|
||
color: #888;
|
||
margin-top: 4px;
|
||
}
|
||
</style>
|