|
|
|
@@ -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 %} |