Parcourir la source

save 处理提示

master
leiyun il y a 4 heures
Parent
révision
20f7243bf0
7 fichiers modifiés avec 324 ajouts et 2 suppressions
  1. +8
    -0
      src/config/router.js
  2. +1
    -1
      src/config/sms.js
  3. +9
    -0
      src/controller/admin/system/role.js
  4. +86
    -0
      src/controller/admin/tag.js
  5. +6
    -1
      src/controller/base.js
  6. +10
    -0
      view/admin/common/_sidebar.html
  7. +204
    -0
      view/admin/tag_index.html

+ 8
- 0
src/config/router.js Voir le fichier

@@ -73,6 +73,14 @@ module.exports = [
['/api/mp/unreadCount', 'mp/unreadCount'],
['/api/mp/subscribeConfig', 'mp/subscribeConfig'],

// 瘤种管理
['/admin/tag', 'admin/tag/index'],
['/admin/tag/list', 'admin/tag/list'],
['/admin/tag/add', 'admin/tag/add', 'post'],
['/admin/tag/edit', 'admin/tag/edit', 'post'],
['/admin/tag/delete', 'admin/tag/delete', 'post'],
['/admin/tag/sort', 'admin/tag/sort', 'post'],

// 内容管理
['/admin/content', 'admin/content/index'],
['/admin/content/list', 'admin/content/list'],


+ 1
- 1
src/config/sms.js Voir le fichier

@@ -26,7 +26,7 @@ module.exports = {
length: 6,
expireMinutes: 5,
intervalSeconds: 60,
dailyLimit: 10
dailyLimit: 3
},

// 是否启用短信发送(关闭时仅打印日志不真实发送)


+ 9
- 0
src/controller/admin/system/role.js Voir le fichier

@@ -259,6 +259,15 @@ module.exports = class extends Base {
{ name: '删除', key: 'patient:delete' }
]
},
{
name: '瘤种管理', key: 'tag',
children: [
{ name: '查看', key: 'tag:view' },
{ name: '新增', key: 'tag:add' },
{ name: '编辑', key: 'tag:edit' },
{ name: '删除', key: 'tag:delete' }
]
},
{
name: '内容管理', key: 'content',
children: [


+ 86
- 0
src/controller/admin/tag.js Voir le fichier

@@ -0,0 +1,86 @@
const Base = require('../base');

const CONFIG_KEY = 'tag_options';

module.exports = class extends Base {
// 瘤种管理页面
async indexAction() {
this.assign('currentPage', 'tag');
this.assign('pageTitle', '瘤种管理');
this.assign('breadcrumb', [{ name: '瘤种管理' }]);
this.assign('adminUser', this.adminUser || {});
this.assign('canAdd', this.isSuperAdmin || (this.userPermissions || []).includes('tag:add'));
this.assign('canEdit', this.isSuperAdmin || (this.userPermissions || []).includes('tag:edit'));
this.assign('canDelete', this.isSuperAdmin || (this.userPermissions || []).includes('tag:delete'));
return this.display();
}

// 获取瘤种列表
async listAction() {
const tags = await this.model('sys_config').getByKey(CONFIG_KEY) || [];
return this.success(tags);
}

// 新增瘤种
async addAction() {
const { name } = this.post();
if (!name || !name.trim()) return this.fail('瘤种名称不能为空');

const tags = await this.model('sys_config').getByKey(CONFIG_KEY) || [];
if (tags.includes(name.trim())) return this.fail('该瘤种已存在');

tags.push(name.trim());
await this.model('sys_config').setByKey(CONFIG_KEY, tags, '瘤种选项');

await this.log('add', '瘤种管理', `新增瘤种「${name.trim()}」`);
return this.success();
}

// 编辑瘤种
async editAction() {
const { oldName, newName } = this.post();
if (!oldName || !newName || !newName.trim()) return this.fail('参数错误');

const tags = await this.model('sys_config').getByKey(CONFIG_KEY) || [];
const idx = tags.indexOf(oldName);
if (idx === -1) return this.fail('原瘤种不存在');
if (oldName !== newName.trim() && tags.includes(newName.trim())) return this.fail('该瘤种已存在');

tags[idx] = newName.trim();
await this.model('sys_config').setByKey(CONFIG_KEY, tags, '瘤种选项');

// 同步更新患者表中的瘤种名称
if (oldName !== newName.trim()) {
await this.model('patient').where({ tag: oldName }).update({ tag: newName.trim() });
}

await this.log('edit', '瘤种管理', `编辑瘤种「${oldName}」→「${newName.trim()}」`);
return this.success();
}

// 删除瘤种
async deleteAction() {
const { name } = this.post();
if (!name) return this.fail('参数错误');

const tags = await this.model('sys_config').getByKey(CONFIG_KEY) || [];
const idx = tags.indexOf(name);
if (idx === -1) return this.fail('瘤种不存在');

tags.splice(idx, 1);
await this.model('sys_config').setByKey(CONFIG_KEY, tags, '瘤种选项');

await this.log('delete', '瘤种管理', `删除瘤种「${name}」`);
return this.success();
}

// 排序瘤种
async sortAction() {
const { tags } = this.post();
if (!Array.isArray(tags)) return this.fail('参数错误');

await this.model('sys_config').setByKey(CONFIG_KEY, tags, '瘤种选项');
await this.log('edit', '瘤种管理', '调整瘤种排序');
return this.success();
}
};

+ 6
- 1
src/controller/base.js Voir le fichier

@@ -201,7 +201,12 @@ module.exports = class extends think.Controller {
});

if (!sendResult.success) {
return { success: false, message: sendResult.message || '短信发送失败' };
// 腾讯云超限错误转为友好提示
const msg = sendResult.message || '';
if (msg.toLowerCase().includes('exceeds the upper limit')) {
return { success: false, message: '今日发送次数已达上限' };
}
return { success: false, message: msg || '短信发送失败' };
}

// 存入缓存


+ 10
- 0
view/admin/common/_sidebar.html Voir le fichier

@@ -22,6 +22,16 @@
</div>
{% endif %}

{# 瘤种管理 #}
{% if isSuperAdmin or (userPermissions and 'tag' in userPermissions) %}
<div class="menu-group">
<a class="menu-item {% if currentPage == 'tag' %}active{% endif %}" href="/admin/tag.html">
<span class="menu-icon"><svg viewBox="0 0 1024 1024" width="18" height="18"><path d="M483.2 32.4L108.8 406.8c-25 25-25 65.6 0 90.6l418.4 418.4c25 25 65.6 25 90.6 0l374.4-374.4c12-12 18.8-28.4 18.8-45.2V128c0-35.2-28.8-64-64-64H528.4c-16.8 0-33.2 6.8-45.2 18.4zM736 352c-44.2 0-80-35.8-80-80s35.8-80 80-80 80 35.8 80 80-35.8 80-80 80z" fill="currentColor"/></svg></span>
<span>瘤种管理</span>
</a>
</div>
{% endif %}

{# 内容管理 #}
{% if isSuperAdmin or (userPermissions and 'content' in userPermissions) %}
<div class="menu-group">


+ 204
- 0
view/admin/tag_index.html Voir le fichier

@@ -0,0 +1,204 @@
{% extends "./layout.html" %}

{% block title %}瘤种管理{% endblock %}

{% block css %}
<style>
.drag-handle { cursor: move; color: #999; font-size: 18px; }
.drag-handle:hover { color: #ff7800; }
.sortable-ghost { opacity: 0.4; background: #fff7ed; }
</style>
{% endblock %}

{% block content %}
<div id="tagApp" v-cloak>
<el-card shadow="never">
<template #header>
<div class="flex items-center justify-between">
<el-button v-if="canAdd" type="primary" @click="showAdd">+ 新增瘤种</el-button>
<span class="text-xs text-gray-400">拖拽行可调整排序</span>
</div>
</template>

<el-table :data="tableData" v-loading="loading" stripe border row-key="__idx">
<el-table-column width="60" align="center">
<template #header>排序</template>
<template #default>
<span class="drag-handle">⠿</span>
</template>
</el-table-column>
<el-table-column type="index" label="序号" width="80" align="center"></el-table-column>
<el-table-column label="瘤种名称">
<template #default="{ row }">${ row.name }</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template #default="{ row }">
<el-button v-if="canEdit" type="primary" link @click="showEdit(row.name)">编辑</el-button>
<el-button v-if="canDelete" type="danger" link @click="handleDelete(row.name)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>

<!-- 新增/编辑弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="440px" destroy-on-close draggable :close-on-click-modal="false">
<el-form :model="form" label-width="90px">
<el-form-item label="瘤种名称" required>
<el-input v-model="form.name" placeholder="请输入瘤种名称" maxlength="20" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleSave" :loading="saving">确定</el-button>
</template>
</el-dialog>
</div>
{% endblock %}

{% block js %}
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
<script>
const canAdd = {{ canAdd | dump | safe }};
const canEdit = {{ canEdit | dump | safe }};
const canDelete = {{ canDelete | dump | safe }};

const { createApp, ref, reactive, onMounted, nextTick } = Vue;

const app = createApp({
delimiters: ['${', '}'],
setup() {
const loading = ref(false);
const tableData = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref('新增瘤种');
const saving = ref(false);
const form = reactive({ name: '', oldName: '' });
let sortableInstance = null;

async function loadList() {
loading.value = true;
try {
const res = await fetch('/admin/tag/list').then(r => r.json());
if (res.code === 0) {
const list = res.data || [];
tableData.value = list.map(function(name, i) { return { name: name, __idx: String(i) }; });
nextTick(function() { initSortable(); });
} else {
ElementPlus.ElMessage.error(res.msg || '加载失败');
}
} finally { loading.value = false; }
}

function initSortable() {
if (sortableInstance) { sortableInstance.destroy(); sortableInstance = null; }
var tbody = document.querySelector('#tagApp .el-table__body-wrapper tbody');
if (!tbody) return;
sortableInstance = Sortable.create(tbody, {
handle: '.drag-handle',
animation: 150,
ghostClass: 'sortable-ghost',
onEnd: function(evt) {
if (evt.oldIndex === evt.newIndex) return;
var list = tableData.value.slice();
var item = list.splice(evt.oldIndex, 1)[0];
list.splice(evt.newIndex, 0, item);
tableData.value = list;
saveSort(list.map(function(t) { return t.name; }));
}
});
}

async function saveSort(tags) {
try {
var res = await fetch('/admin/tag/sort', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tags: tags })
}).then(function(r) { return r.json(); });
if (res.code === 0) {
ElementPlus.ElMessage.success('排序已保存');
} else {
ElementPlus.ElMessage.error(res.msg || '排序保存失败');
loadList();
}
} catch (e) {
ElementPlus.ElMessage.error('排序保存失败');
loadList();
}
}

function showAdd() {
dialogTitle.value = '新增瘤种';
form.name = '';
form.oldName = '';
dialogVisible.value = true;
}

function showEdit(name) {
dialogTitle.value = '编辑瘤种';
form.name = name;
form.oldName = name;
dialogVisible.value = true;
}

async function handleSave() {
if (!form.name.trim()) {
ElementPlus.ElMessage.warning('瘤种名称不能为空');
return;
}
saving.value = true;
try {
var url, body;
if (form.oldName) {
url = '/admin/tag/edit';
body = { oldName: form.oldName, newName: form.name };
} else {
url = '/admin/tag/add';
body = { name: form.name };
}
var res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
}).then(function(r) { return r.json(); });
if (res.code === 0) {
ElementPlus.ElMessage.success('保存成功');
dialogVisible.value = false;
loadList();
} else {
ElementPlus.ElMessage.error(res.msg || '保存失败');
}
} finally { saving.value = false; }
}

async function handleDelete(name) {
try {
await ElementPlus.ElMessageBox.confirm('确定要删除瘤种「' + name + '」吗?删除后不会影响已有患者的瘤种标记。', '提示', { type: 'warning' });
var res = await fetch('/admin/tag/delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: name })
}).then(function(r) { return r.json(); });
if (res.code === 0) {
ElementPlus.ElMessage.success('删除成功');
loadList();
} else {
ElementPlus.ElMessage.error(res.msg || '删除失败');
}
} catch (e) {}
}

onMounted(function() { loadList(); });

return {
loading, tableData, dialogVisible, dialogTitle, saving, form,
canAdd, canEdit, canDelete,
loadList, showAdd, showEdit, handleSave, handleDelete
};
}
});

app.use(ElementPlus, { locale: ElementPlusLocaleZhCn });
app.mount('#tagApp');
</script>
{% endblock %}

Chargement…
Annuler
Enregistrer