25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 
 
 
 
 

212 satır
8.6 KiB

  1. {% extends "../layout.html" %}
  2. {% block title %}{{ columnInfo.name if columnInfo else '岗位管理' }}{% endblock %}
  3. {% block content %}
  4. <div id="jobApp">
  5. <el-card shadow="never">
  6. <template #header>
  7. <div class="flex items-center justify-between">
  8. <div class="flex items-center gap-2">
  9. <span class="font-medium">{{ columnInfo.name if columnInfo else '岗位管理' }}</span>
  10. <el-tag size="small">岗位管理</el-tag>
  11. </div>
  12. <el-button type="primary" @click="openDialog()">+ 发布岗位</el-button>
  13. </div>
  14. </template>
  15. <!-- 筛选栏 -->
  16. <div class="flex items-center gap-4 mb-4">
  17. <el-input v-model="query.keyword" placeholder="岗位名称..." style="width:180px;" clearable @keyup.enter="loadList"></el-input>
  18. <el-select v-model="query.status" placeholder="状态" style="width:110px;" clearable>
  19. <el-option label="招聘中" :value="1"></el-option>
  20. <el-option label="已关闭" :value="0"></el-option>
  21. </el-select>
  22. <el-button type="primary" @click="loadList">搜索</el-button>
  23. <el-button @click="resetQuery">重置</el-button>
  24. </div>
  25. <!-- 表格 -->
  26. <el-table :data="list" v-loading="loading" border>
  27. <el-table-column prop="name" label="岗位名称" min-width="140"></el-table-column>
  28. <el-table-column prop="department" label="部门" width="120"></el-table-column>
  29. <el-table-column prop="location" label="工作地点" width="100"></el-table-column>
  30. <el-table-column prop="count" label="人数" width="80" align="center"></el-table-column>
  31. <el-table-column prop="salary" label="薪资" width="120"></el-table-column>
  32. <el-table-column label="状态" width="100" align="center">
  33. <template #default="{ row }">
  34. <el-tag :type="row.status ? 'success' : 'info'" size="small">${ row.status ? '招聘中' : '已关闭' }</el-tag>
  35. </template>
  36. </el-table-column>
  37. <el-table-column prop="create_time" label="发布日期" width="120">
  38. <template #default="{ row }">
  39. ${ row.create_time?.slice(0, 10) }
  40. </template>
  41. </el-table-column>
  42. <el-table-column label="操作" width="140" fixed="right">
  43. <template #default="{ row }">
  44. <el-button type="primary" link size="small" @click="openDialog(row)">编辑</el-button>
  45. <el-button type="danger" link size="small" @click="deleteItem(row)">删除</el-button>
  46. </template>
  47. </el-table-column>
  48. </el-table>
  49. <!-- 分页 -->
  50. <div class="flex justify-end mt-4" v-if="total > pageSize">
  51. <el-pagination background layout="prev, pager, next" :total="total" :page-size="pageSize" v-model:current-page="page" @current-change="loadList"></el-pagination>
  52. </div>
  53. </el-card>
  54. <!-- 新增/编辑弹窗 -->
  55. <el-dialog v-model="dialogVisible" :title="dialogTitle" width="620px" destroy-on-close draggable top="5vh">
  56. <div class="dialog-scroll-body">
  57. <el-form :model="form" label-width="90px">
  58. <el-form-item label="岗位名称" required>
  59. <el-input v-model="form.name" placeholder="请输入岗位名称"></el-input>
  60. </el-form-item>
  61. <div class="flex gap-4">
  62. <el-form-item label="所属部门" required class="flex-1">
  63. <el-input v-model="form.department" placeholder="如:项目部"></el-input>
  64. </el-form-item>
  65. <el-form-item label="工作地点" required class="flex-1">
  66. <el-input v-model="form.location" placeholder="如:北京"></el-input>
  67. </el-form-item>
  68. </div>
  69. <div class="flex gap-4">
  70. <el-form-item label="招聘人数" class="flex-1">
  71. <el-input-number v-model="form.count" :min="1" style="width:100%;"></el-input-number>
  72. </el-form-item>
  73. <el-form-item label="薪资范围" class="flex-1">
  74. <el-input v-model="form.salary" placeholder="如:8K-12K / 面议"></el-input>
  75. </el-form-item>
  76. </div>
  77. <el-form-item label="岗位职责" required>
  78. <el-input v-model="form.duty" type="textarea" :rows="4" placeholder="请输入岗位职责描述"></el-input>
  79. </el-form-item>
  80. <el-form-item label="任职要求" required>
  81. <el-input v-model="form.requirement" type="textarea" :rows="4" placeholder="请输入任职要求"></el-input>
  82. </el-form-item>
  83. <el-form-item label="状态">
  84. <el-switch v-model="form.status" :active-value="1" :inactive-value="0" active-text="招聘中" inactive-text="已关闭"></el-switch>
  85. </el-form-item>
  86. </el-form>
  87. </div>
  88. <template #footer>
  89. <el-button @click="dialogVisible = false">取消</el-button>
  90. <el-button type="primary" @click="saveItem" :loading="saving">保存</el-button>
  91. </template>
  92. </el-dialog>
  93. </div>
  94. <style>
  95. .dialog-scroll-body { max-height: 65vh; overflow-y: auto; overflow-x: hidden; }
  96. </style>
  97. {% endblock %}
  98. {% block js %}
  99. <script>
  100. const col = '{{ col }}';
  101. const { createApp, ref, reactive, onMounted } = Vue;
  102. const app = createApp({
  103. delimiters: ['${', '}'],
  104. setup() {
  105. const loading = ref(false);
  106. const list = ref([]);
  107. const total = ref(0);
  108. const page = ref(1);
  109. const pageSize = ref(20);
  110. const query = reactive({ keyword: '', status: '' });
  111. const dialogVisible = ref(false);
  112. const dialogTitle = ref('发布岗位');
  113. const saving = ref(false);
  114. const defaultForm = { id: null, name: '', department: '', location: '', count: 1, duty: '', requirement: '', salary: '', status: 1 };
  115. const form = reactive({ ...defaultForm });
  116. async function loadList() {
  117. loading.value = true;
  118. try {
  119. const params = new URLSearchParams({ col, page: page.value, pageSize: pageSize.value, ...query });
  120. const res = await fetch('/admin/content/job/list?' + params).then(r => r.json());
  121. if (res.code === 0) {
  122. list.value = res.data.data || [];
  123. total.value = res.data.count || 0;
  124. }
  125. } finally { loading.value = false; }
  126. }
  127. function resetQuery() {
  128. query.keyword = '';
  129. query.status = '';
  130. page.value = 1;
  131. loadList();
  132. }
  133. function openDialog(item) {
  134. if (item) {
  135. dialogTitle.value = '编辑岗位';
  136. Object.assign(form, {
  137. id: item.id, name: item.name, department: item.department || '',
  138. location: item.location || '', count: item.count || 1,
  139. duty: item.duty || '', requirement: item.requirement || '',
  140. salary: item.salary || '', status: item.status
  141. });
  142. } else {
  143. dialogTitle.value = '发布岗位';
  144. Object.assign(form, { ...defaultForm });
  145. }
  146. dialogVisible.value = true;
  147. }
  148. async function saveItem() {
  149. if (!form.name.trim()) { ElementPlus.ElMessage.warning('请输入岗位名称'); return; }
  150. if (!form.department.trim()) { ElementPlus.ElMessage.warning('请输入所属部门'); return; }
  151. if (!form.location.trim()) { ElementPlus.ElMessage.warning('请输入工作地点'); return; }
  152. if (!form.duty.trim()) { ElementPlus.ElMessage.warning('请输入岗位职责'); return; }
  153. if (!form.requirement.trim()) { ElementPlus.ElMessage.warning('请输入任职要求'); return; }
  154. saving.value = true;
  155. try {
  156. const url = form.id ? '/admin/content/job/edit' : '/admin/content/job/add';
  157. const body = { ...form, col };
  158. const res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }).then(r => r.json());
  159. if (res.code === 0) {
  160. ElementPlus.ElMessage.success('保存成功');
  161. dialogVisible.value = false;
  162. loadList();
  163. } else {
  164. ElementPlus.ElMessage.error(res.msg || '保存失败');
  165. }
  166. } finally { saving.value = false; }
  167. }
  168. async function deleteItem(item) {
  169. try {
  170. await ElementPlus.ElMessageBox.confirm('确定删除岗位"' + item.name + '"?', '提示', { type: 'warning' });
  171. const res = await fetch('/admin/content/job/delete', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: item.id }) }).then(r => r.json());
  172. if (res.code === 0) {
  173. ElementPlus.ElMessage.success('删除成功');
  174. loadList();
  175. } else {
  176. ElementPlus.ElMessage.error(res.msg || '删除失败');
  177. }
  178. } catch {}
  179. }
  180. onMounted(() => loadList());
  181. return {
  182. loading, list, total, page, pageSize, query,
  183. dialogVisible, dialogTitle, saving, form,
  184. loadList, resetQuery, openDialog, saveItem, deleteItem
  185. };
  186. }
  187. });
  188. app.use(ElementPlus, { locale: ElementPlusLocaleZhCn });
  189. app.mount('#jobApp');
  190. </script>
  191. {% endblock %}