admin.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. <template>
  2. <!-- 管理员管理首页 -->
  3. <div class="about main-background main-background-type0">
  4. <div v-if="!isInMiniProgram" class="nav-placeholder">
  5. </div>
  6. <!-- 表单 -->
  7. <section class="main-section large">
  8. <div class="content">
  9. <div v-if="!isInMiniProgram" class="title">
  10. <h2>管理员管理</h2>
  11. </div>
  12. <a-tabs v-model:activeKey="activeKey" centered>
  13. <template #renderTabBar="{ DefaultTabBar, ...props }">
  14. <component v-if="!isInMiniProgram" :is="DefaultTabBar" v-bind="props" />
  15. </template>
  16. <a-tab-pane key="1" tab="传承人列表">
  17. <CommonListBlock
  18. ref="listRef"
  19. :showTotal="true"
  20. :rowCount="1"
  21. :rowType="5"
  22. :dropDownNames="[
  23. {
  24. options: categoryData.content.value ?? [],
  25. label: '分类',
  26. defaultSelectedValue: lastValueCategory,
  27. },
  28. {
  29. options: computedCategoryOptions,
  30. label: '状态',
  31. defaultSelectedValue: lastValueStatus,
  32. },
  33. {
  34. options: typesettingData,
  35. label: '排版',
  36. defaultSelectedValue: 0,
  37. },
  38. {
  39. options: selfAssessmentLevelOptions,
  40. label: '等级',
  41. defaultSelectedValue: lastAgreementLevel,
  42. },
  43. ]"
  44. :load="(page: number, pageSize: number, _, searchText: string, dropDownValues: number[]) => loadInheritorData(page, pageSize, dropDownValues, searchText)"
  45. :showDetail="(item) => router.push({ name: 'FormInheritor', query: { id: item.id } })"
  46. >
  47. <template #itemRight="{ item }">
  48. <AdminItemState :item="item" />
  49. <a-button type="link">编辑/审核</a-button>
  50. <a-button v-if="!authStore.isReviewer" type="link" @click.stop="handleCopyAccount(item)">传承人账号</a-button>
  51. </template>
  52. </CommonListBlock>
  53. </a-tab-pane>
  54. <a-tab-pane key="2" tab="非遗项目列表">
  55. <CommonListBlock
  56. ref="listRef"
  57. :showTotal="true"
  58. :rowCount="1"
  59. :rowType="5"
  60. :dropDownNames="[
  61. {
  62. options: categoryData.content.value ?? [],
  63. label: '分类',
  64. defaultSelectedValue: lastValueCategory,
  65. },
  66. {
  67. options: computedCategoryOptions,
  68. label: '状态',
  69. defaultSelectedValue: lastValueStatus,
  70. },
  71. {
  72. options: typesettingData,
  73. label: '排版',
  74. defaultSelectedValue: 0,
  75. },
  76. {
  77. options: selfAssessmentLevelOptions,
  78. label: '等级',
  79. defaultSelectedValue: lastAgreementLevel,
  80. },
  81. ]"
  82. :load="(page: number, pageSize: number, _, searchText: string, dropDownValues: number[]) => loadIchData(page, pageSize, dropDownValues, searchText)"
  83. :showDetail="(item) => router.push({ name: 'FormIch', query: { id: item.id } })"
  84. >
  85. <template #itemRight="{ item }">
  86. <AdminItemState :item="item" />
  87. <a-button type="link" @click.stop="router.push({ name: 'FormIch', query: { id: item.id } })">编辑/审核</a-button>
  88. <a-button type="link" @click.stop="handleGoWorks(item)">非遗项目作品</a-button>
  89. <a-button type="link" @click.stop="handleGoSeminar(item)">添加/修改传习所</a-button>
  90. </template>
  91. </CommonListBlock>
  92. </a-tab-pane>
  93. <a-tab-pane key="3" tab="传习所列表">
  94. <div v-if="false" class="flex justify-end">
  95. <a-button type="primary" @click="router.push({ name: 'FormSeminar' })">+ 新增</a-button>
  96. </div>
  97. <CommonListBlock
  98. ref="listRef"
  99. :showTotal="true"
  100. :rowCount="1"
  101. :rowType="5"
  102. :load="(page: number, pageSize: number, _, searchText: string, dropDownValues: number[]) => loadSeminarData(page, pageSize, dropDownValues, searchText)"
  103. :showDetail="(item) => router.push({ name: 'FormSeminar', query: { id: item.id } })"
  104. >
  105. <template #itemRight="{ item }">
  106. <a-button type="link" @click.stop="router.push({ name: 'FormSeminar', query: { id: item.id } })">编辑</a-button>
  107. </template>
  108. <template #headLeft>
  109. <InfoCircleOutlined />
  110. 请在非遗项目列表中添加传习所
  111. </template>
  112. </CommonListBlock>
  113. </a-tab-pane>
  114. <!-- <a-tab-pane key="5" tab="老字号">
  115. <a-empty description="暂无数据" />
  116. </a-tab-pane>
  117. <a-tab-pane key="6" tab="传统村落">
  118. <a-empty description="暂无数据" />
  119. </a-tab-pane>
  120. <a-tab-pane key="7" tab="区级非遗传承人">
  121. <a-empty description="暂无数据" />
  122. </a-tab-pane> -->
  123. <a-tab-pane key="9" tab="传承协议签署">
  124. <CommonListBlock
  125. ref="listRef"
  126. :show-total="true"
  127. :row-count="1"
  128. :row-type="5"
  129. :page-size="10"
  130. :drop-down-names="[
  131. {
  132. options: agreementProgressOptions,
  133. label: '状态',
  134. defaultSelectedValue: lastAgreementProgress,
  135. },
  136. {
  137. options: selfAssessmentLevelOptions,
  138. label: '等级',
  139. defaultSelectedValue: lastAgreementLevel,
  140. },
  141. ]"
  142. :load="(page: number, pageSize: number, _tag: number, searchText: string, drop: number[]) => loadAgreementSignAdminList(page, pageSize, searchText, drop)"
  143. :show-detail="(item) => router.push({ name: 'CollectAgreementSign', query: { id: item.agreementId && item.agreementId > 0 ? item.agreementId : 0, userId: item.userId ?? 0 } })"
  144. >
  145. <template #itemRight="{ item }">
  146. <span class="mr-3 text-sm text-gray-600">{{ agreementProgressLabel(item.progress) }}</span>
  147. <a-button type="link" @click.stop="router.push({ name: 'CollectAgreementSign', query: { id: item.agreementId && item.agreementId > 0 ? item.agreementId : 0, userId: item.userId ?? 0 } })">编辑</a-button>
  148. </template>
  149. </CommonListBlock>
  150. </a-tab-pane>
  151. <a-tab-pane key="8" tab="自查评估表">
  152. <EvaluationFormList ref="listRef" />
  153. </a-tab-pane>
  154. <a-tab-pane v-if="false" key="4" tab="重点区域">
  155. <div class="flex justify-end">
  156. <a-button type="primary" :disabled="true" @click="router.push({ name: 'FormWork' })">+ 新增</a-button>
  157. </div>
  158. <CommonListBlock
  159. ref="listRef"
  160. :showTotal="true"
  161. :rowCount="1"
  162. :rowType="5"
  163. :dropDownNames="[{
  164. options: categoryData.content.value ?? [],
  165. label: '分类',
  166. defaultSelectedValue: 0,
  167. }]"
  168. :load="(page: number, pageSize: number, _, searchText: string, dropDownValues: number[]) => loadAreaData(page, pageSize, dropDownValues, searchText)"
  169. :showDetail="(item) => router.push({ name: 'FormIch', query: { id: item.id } })"
  170. >
  171. <template #itemRight="{ item }">
  172. <AdminItemState :item="item" />
  173. <a-button type="link" @click.stop="router.push({ name: 'FormIch', query: { id: item.id } })">编辑</a-button>
  174. <a-button type="link" @click.stop="handleGoWorks(item)">非遗项目作品</a-button>
  175. </template>
  176. </CommonListBlock>
  177. </a-tab-pane>
  178. </a-tabs>
  179. </div>
  180. </section>
  181. </div>
  182. </template>
  183. <script setup lang="ts">
  184. import { computed, onMounted, ref, watch } from 'vue';
  185. import { useRoute, useRouter } from 'vue-router';
  186. import { useAuthStore } from '@/stores/auth';
  187. import { message, Modal } from 'ant-design-vue';
  188. import type { GetContentListItem } from '@/api/CommonContent';
  189. import useClipboard from 'vue-clipboard3';
  190. import CommonContent, { GetContentListParams } from '@/api/CommonContent';
  191. import CommonListBlock, { type DropdownCommonItem } from '@/components/content/CommonListBlock.vue';
  192. import InheritorContent from '@/api/inheritor/InheritorContent';
  193. import AssessmentContentApi from '@/api/collect/AssessmentContent';
  194. import AdminItemState from './components/AdminItemState.vue';
  195. import EvaluationFormList from './collect/assessment/evaluation-form-list.vue';
  196. import { InfoCircleOutlined } from '@ant-design/icons-vue';
  197. import { useSimpleDataLoader } from '@/composeables/useSimpleDataLoader';
  198. import { useMemorizeVar } from '@/composeables/useMemorizeVar';
  199. import { isInMiniProgram } from '@/composeables/MiniProgramIng.ts';
  200. const { toClipboard } = useClipboard();
  201. const router = useRouter();
  202. const route = useRoute();
  203. const authStore = useAuthStore();
  204. const activeKey = ref(route.query.tab as string || '1');
  205. const inheritorData = ref<GetContentListItem[]>([]);
  206. const listRef = ref<any>(null);
  207. onMounted(() => {
  208. switch (activeKey.value) {
  209. case '1': document.title = '传承人管理'; break;
  210. case '3': document.title = '传习所管理'; break;
  211. case '5': document.title = '老字号管理'; break;
  212. case '9': document.title = '传承协议签署'; break;
  213. case '8': document.title = '自查评估表'; break;
  214. }
  215. });
  216. watch(() => authStore.userId, (newV) => {
  217. if (newV) {
  218. setTimeout(() => {
  219. listRef.value?.reload();
  220. }, 1000);
  221. }
  222. });
  223. const { variable: lastValueCategory } = useMemorizeVar('categoryLastSelectValue', 0);
  224. const { variable: lastValueStatus } = useMemorizeVar('statusLastSelectValue', -10);
  225. const { variable: lastAgreementProgress } = useMemorizeVar('adminUserAgreementProgress', -100);
  226. const { variable: lastAgreementLevel } = useMemorizeVar('adminUserAgreementLevel', 0);
  227. const selfAssessmentLevelOptions: DropdownCommonItem[] = [
  228. { id: 0, name: '全部等级' },
  229. { id: 23, name: '国家级' },
  230. { id: 24, name: '省级' },
  231. { id: 25, name: '市级' },
  232. ];
  233. /** 传承人传承协议列表:进度筛选(与 ich/check/getUserAgreement 一致) */
  234. const agreementProgressOptions: DropdownCommonItem[] = [
  235. { id: -100, name: '全部状态' },
  236. { id: -1, name: '未提交' },
  237. { id: 0, name: '草稿' },
  238. { id: 1, name: '已提交审核' },
  239. { id: 2, name: '审核完成' },
  240. ];
  241. function agreementProgressLabel(progress: number | null | undefined) {
  242. if (progress === null || progress === undefined)
  243. return '未填写';
  244. const hit = agreementProgressOptions.find((o) => o.id === progress);
  245. return hit?.name ?? `进度 ${progress}`;
  246. }
  247. const computedCategoryOptions = computed<DropdownCommonItem[]>(() => {
  248. if (authStore.isReviewer) {
  249. return [
  250. { name: '全部状态', id: -10 },
  251. { name: '待审核', id: 1 },
  252. { name: '已通过', id: 2 },
  253. ]
  254. }
  255. if (authStore.isAdmin) {
  256. return [
  257. { name: '全部状态', id: -10 },
  258. { name: '保存未审核', id: -2 },
  259. { name: '审核退回', id: -1 },
  260. { name: '待初审', id: 0 },
  261. { name: '初审通过待专家审核', id: 1 },
  262. { name: '专家审核通过', id: 2 },
  263. ]
  264. }
  265. return [];
  266. })
  267. const typesettingData = [
  268. {
  269. id: -1,
  270. name: '全部',
  271. },
  272. {
  273. id: 0,
  274. name: '未排版',
  275. },
  276. {
  277. id: 1,
  278. name: '已排版',
  279. },
  280. ];
  281. watch(activeKey, (newValue) => {
  282. router.replace({ query: { tab: newValue } });
  283. })
  284. const categoryData = useSimpleDataLoader(async () => {
  285. const arr = (await CommonContent.getCategoryList(4)).map((item) => ({
  286. id: item.id,
  287. name: item.title,
  288. }));
  289. arr.unshift({
  290. id: 0,
  291. name: '全部',
  292. });
  293. return arr;
  294. })
  295. async function loadInheritorData(page: number, pageSize: number, dropDownValues: number[], searchText: string) {
  296. lastValueCategory.value = dropDownValues[0];
  297. lastValueStatus.value = dropDownValues[1];
  298. const submitList = await InheritorContent.getInheritorSubmtList(7);
  299. const res = await CommonContent.getContentList(
  300. new GetContentListParams()
  301. .setModelId(7)
  302. .setMainBodyColumnId(38)
  303. .setKeywords(searchText)
  304. .setSelfValues({
  305. ichType: dropDownValues[0] == 0 ? undefined: dropDownValues[0],
  306. progress: dropDownValues[1] <= -5 ? undefined: dropDownValues[1],
  307. typesetting: dropDownValues[2] < 0 ? undefined: dropDownValues[2],
  308. level: dropDownValues[3] <= 0 ? undefined: dropDownValues[3],
  309. region: authStore.userInfo?.regionId,
  310. }),
  311. page,
  312. pageSize
  313. );
  314. return {
  315. page,
  316. total: res.total,
  317. data: res.list.map((item) => {
  318. const submitInfo = submitList.find((item2) => item2.id == item.id);
  319. return {
  320. ...item,
  321. desc: `${item.ichName} ${item.levelText} ${item.batchText}`,
  322. hasSubmit: Boolean(submitInfo),
  323. ...submitInfo,
  324. }
  325. }),
  326. }
  327. }
  328. async function loadIchData(page: number, pageSize: number, dropDownValues: number[], searchText: string) {
  329. lastValueCategory.value = dropDownValues[0];
  330. lastValueStatus.value = dropDownValues[1];
  331. const submitList = await InheritorContent.getInheritorSubmtList(2);
  332. const res = await CommonContent.getContentList(
  333. new GetContentListParams()
  334. .setModelId(2)
  335. .setKeywords(searchText)
  336. .setSelfValues({
  337. ichType: dropDownValues[0] == 0 ? undefined: dropDownValues[0],
  338. progress: dropDownValues[1] <= -5 ? undefined: dropDownValues[1],
  339. typesetting: dropDownValues[2] < 0 ? undefined: dropDownValues[2],
  340. level: dropDownValues[3] <= 0 ? undefined: dropDownValues[3],
  341. region: authStore.userInfo?.regionId,
  342. }),
  343. page,
  344. pageSize
  345. );
  346. return {
  347. page,
  348. total: res.total,
  349. data: res.list.map((item) => {
  350. const submitInfo = submitList.find((item2) => item2.id == item.id);
  351. return {
  352. ...item,
  353. desc: `${item.ichTypeText} - ${item.levelText} ${item.batchText}`,
  354. hasSubmit: Boolean(submitInfo),
  355. ...submitInfo,
  356. }
  357. }),
  358. }
  359. }
  360. async function loadSeminarData(page: number, pageSize: number, dropDownValues: number[], searchText: string) {
  361. if (page === 1) {
  362. const res = await InheritorContent.getIchSeminarInfo({
  363. ichId: undefined,
  364. keywords: searchText,
  365. });
  366. return {
  367. page,
  368. total: pageSize,
  369. data: res.map((item) => ({
  370. ...item,
  371. desc: item.address,
  372. })),
  373. }
  374. }
  375. return {
  376. page,
  377. total: 0,
  378. data: [],
  379. }
  380. }
  381. async function loadAreaData(page: number, pageSize: number, dropDownValues: number[], searchText: string) {
  382. return {
  383. page,
  384. total: 0,
  385. data: [],
  386. }
  387. }
  388. /** 管理员:传承人传承协议分页(ich/check/getUserAgreement) */
  389. async function loadAgreementSignAdminList(page: number, pageSize: number, searchText: string, dropDownValues: number[]) {
  390. const pv = dropDownValues?.[0];
  391. const lv = dropDownValues?.[1];
  392. lastAgreementProgress.value = pv ?? -100;
  393. lastAgreementLevel.value = lv ?? 0;
  394. const progress = pv != null && pv > -50 ? pv : undefined;
  395. const level = lv != null && lv > 0 ? lv : undefined;
  396. const list = await AssessmentContentApi.getUserAgreementList({
  397. year: new Date().getFullYear(),
  398. page,
  399. pageSize,
  400. keywords: searchText?.trim() || undefined,
  401. progress,
  402. level,
  403. });
  404. console.log(list);
  405. return {
  406. page,
  407. total: list.total,
  408. data: list.data.map((row) => ({
  409. ...row,
  410. id: row.id,
  411. agreementId: row.agreementId,
  412. userId: row.userId,
  413. title: row.title,
  414. desc: [row.mobile, row.unit, row.ichTitle].filter(Boolean).join(' · ') || '—',
  415. })),
  416. };
  417. }
  418. async function handleCopyAccount(item: GetContentListItem) {
  419. let result;
  420. try {
  421. result = await InheritorContent.getInheritorAccountInfo(item.id);
  422. if (!result)
  423. throw '该传承人没有账号';
  424. } catch (e) {
  425. Modal.error({
  426. title: '获取账号失败',
  427. content: '' + e,
  428. });
  429. return;
  430. }
  431. const resultString = `传承人${item.title}的账号:\n用户名:${result.username}\n密码:${result.password}\n登录网址:https://zycj.wenlvti.net/#login`;
  432. try {
  433. await toClipboard(resultString);
  434. message.success('复制到剪贴板成功');
  435. } catch (e) {
  436. Modal.error({
  437. title: '复制失败',
  438. content: '复制到剪贴板失败,可能是浏览器不支持或未授权,可手动复制:' + resultString,
  439. });
  440. }
  441. }
  442. function handleGoSeminar(item: GetContentListItem) {
  443. router.push({ name: 'AdminSeminar', query: {
  444. ichId: item.id,
  445. } })
  446. }
  447. function handleGoWorks(item: GetContentListItem) {
  448. router.push({ name: 'AdminWorks', query: {
  449. ichId: item.id,
  450. } })
  451. }
  452. </script>