gist-list.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. <template>
  2. <div v-loading="isLoading" class="hc-full" element-loading-text="加载中...">
  3. <hc-table
  4. :column="tableColumn" :datas="tableData" :index-style="{ width: 60 }" is-check :check-style="{ fixed: true, width: 29 }"
  5. class="hc-project-list-table" @selection-change="tableCheckChange"
  6. >
  7. <template #workPlan="{ row }">
  8. <el-link type="primary" @click="rowNameClick(row)">{{ row.workPlan }}</el-link>
  9. </template>
  10. <template #action="{ row }">
  11. <el-link v-if="isAdminAuth" type="warning" @click="completion(row)">工作完成情况</el-link>
  12. <el-link type="primary" @click="examine(row)">查看</el-link>
  13. <el-link v-if="isAdminAuth" v-del-com:[delTableItem]="row" type="danger">删除</el-link>
  14. <el-link v-yes-com:[deriveTableItem]="row" type="success">导出</el-link>
  15. </template>
  16. </hc-table>
  17. <!-- 查看详情 -->
  18. <hc-drawer v-model="isDrawer" to-id="hc-main-box" is-close>
  19. <hc-card class="hc-project-list-drawer" is-action-btn>
  20. <template #header>
  21. <div class="flex-1 text-center text-[24px] font-bold">工作要点详情</div>
  22. </template>
  23. <div class="hc-project-list-flex">
  24. <div class="hc-project-list-info-table">
  25. <hc-info-table>
  26. <tr>
  27. <hc-info-table-td is-title width="10%" center>项目阶段</hc-info-table-td>
  28. <hc-info-table-td center width="40%">{{ rowDrawerInfo.workFocusStageName }}</hc-info-table-td>
  29. <hc-info-table-td is-title width="10%" center>目标任务</hc-info-table-td>
  30. <hc-info-table-td center width="40%">{{ rowDrawerInfo.targetPlan }}</hc-info-table-td>
  31. </tr>
  32. </hc-info-table>
  33. <hc-info-table v-if="isDrawerType === 'view'" class="mt-[-1px]">
  34. <tr>
  35. <hc-info-table-td is-title width="10%" center>开始年份</hc-info-table-td>
  36. <hc-info-table-td center width="15%">{{ rowDrawerInfo.startYear }}</hc-info-table-td>
  37. <hc-info-table-td is-title width="10%" center>结束年份</hc-info-table-td>
  38. <hc-info-table-td center width="15%">{{ rowDrawerInfo.endYear }}</hc-info-table-td>
  39. <hc-info-table-td is-title width="10%" center>责任单位</hc-info-table-td>
  40. <hc-info-table-td center width="40%">{{ rowDrawerInfo.dutyUnit }}</hc-info-table-td>
  41. </tr>
  42. </hc-info-table>
  43. <hc-info-table class="mt-[-1px]">
  44. <tr>
  45. <hc-info-table-td is-title width="10%" center>工作内容</hc-info-table-td>
  46. <hc-info-table-td center width="90%">{{ rowDrawerInfo.workPlan }}</hc-info-table-td>
  47. </tr>
  48. </hc-info-table>
  49. </div>
  50. <div class="hc-project-list-drawer-year">
  51. <hc-body scrollbar padding="0">
  52. <div class="relative p-2 pt-6">
  53. <hc-card-item class="year-detail" :class="isDrawerType === 'edit' ? 'edit' : ''">
  54. <template #header>
  55. <div class="flex-1 text-center text-[14px]">
  56. <HcDropdown v-model="yearKey" :datas="yearData.table" text="年" :props="{ key: 'year', label: 'year' }" @change="yearChange" />
  57. </div>
  58. </template>
  59. <el-table v-if="yearData.table && yearData.table.length > 0" :data="yearData.table[yearIndex].data" border class="w-full">
  60. <el-table-column prop="month" class-name="line" width="120" align="center">
  61. <template #header>
  62. <div class="hc-table-th-line">
  63. <span class="left">月份</span>
  64. <span class="right">完成情况</span>
  65. </div>
  66. </template>
  67. <template #default="{ row }">{{ row.month }}月</template>
  68. </el-table-column>
  69. <el-table-column prop="progress" label="累计进展情况" align="center">
  70. <template #default="{ row }">
  71. <hc-body v-if="isDrawerType === 'edit'">
  72. <hc-table-input v-model="row.progress" type="textarea" resize="none" :disabled="currentYear < yearKey" />
  73. </hc-body>
  74. <span v-else>{{ row.progress }}</span>
  75. </template>
  76. </el-table-column>
  77. <el-table-column prop="problemInfo" label="存在问题" align="center">
  78. <template #default="{ row }">
  79. <hc-body v-if="isDrawerType === 'edit'">
  80. <hc-table-input v-model="row.problemInfo" type="textarea" resize="none" :disabled="currentYear < yearKey" />
  81. </hc-body>
  82. <span v-else>{{ row.problemInfo }}</span>
  83. </template>
  84. </el-table-column>
  85. <el-table-column prop="workSug" label="工作建议" align="center">
  86. <template #default="{ row }">
  87. <hc-body v-if="isDrawerType === 'edit'">
  88. <hc-table-input v-model="row.workSug" type="textarea" resize="none" :disabled="currentYear < yearKey" />
  89. </hc-body>
  90. <span v-else>{{ row.workSug }}</span>
  91. </template>
  92. </el-table-column>
  93. </el-table>
  94. <hc-info-table v-if="yearData.table && yearData.table.length > 0" class="mt-[-1px]">
  95. <tr>
  96. <hc-info-table-td is-title width="10%" center>联系人</hc-info-table-td>
  97. <hc-info-table-td center width="40%">
  98. <span v-if="isDrawerType === 'view'">{{ yearData.table[yearIndex].unitInfo }}</span>
  99. <el-input v-else v-model="yearData.table[yearIndex].unitInfo" :disabled="currentYear < yearKey" />
  100. </hc-info-table-td>
  101. <hc-info-table-td is-title width="10%" center>联系电话</hc-info-table-td>
  102. <hc-info-table-td center width="40%">
  103. <span v-if="isDrawerType === 'view'">{{ yearData.table[yearIndex].linkInfo }}</span>
  104. <el-input v-else v-model="yearData.table[yearIndex].linkInfo" :disabled="currentYear < yearKey" />
  105. </hc-info-table-td>
  106. </tr>
  107. </hc-info-table>
  108. </hc-card-item>
  109. </div>
  110. </hc-body>
  111. </div>
  112. </div>
  113. <template v-if="isDrawerType === 'edit'" #action>
  114. <el-button type="info" @click="drawerCancel">取消</el-button>
  115. <el-button type="warning" :loading="saveCompletionLoading" @click="saveCompletionClick">保存</el-button>
  116. </template>
  117. </hc-card>
  118. </hc-drawer>
  119. </div>
  120. </template>
  121. <script setup>
  122. import { ref, watch } from 'vue'
  123. import mainApi from '~api/project/gist'
  124. import { deepClone, getArrValue, getObjValue, newDownBlob } from 'js-fast-way'
  125. import dayjs from 'dayjs'
  126. const props = defineProps({
  127. isAdmin: {
  128. type: Boolean,
  129. default: false,
  130. },
  131. loading: {
  132. type: Boolean,
  133. default: false,
  134. },
  135. datas: {
  136. type: Array,
  137. default: () => ([]),
  138. },
  139. })
  140. //事件
  141. const emit = defineEmits(['tap', 'check', 'change'])
  142. const currentYear = new dayjs().format('YYYY')
  143. //监听权限
  144. const isAdminAuth = ref(props.isAdmin)
  145. watch(() => props.isAdmin, (admin) => {
  146. isAdminAuth.value = admin
  147. })
  148. //监听数据
  149. const tableData = ref(props.datas)
  150. watch(() => props.datas, (data) => {
  151. tableData.value = data
  152. })
  153. //监听加载
  154. const isLoading = ref(props.loading)
  155. watch(() => props.loading, (res) => {
  156. isLoading.value = res
  157. })
  158. //表头
  159. const tableColumn = [
  160. { key: 'workFocusStageName', name: '项目阶段', width: 100 },
  161. { key: 'targetPlan', name: '目标任务', width: 140 },
  162. { key: 'workPlan', name: '工作内容' },
  163. { key: 'startYear', name: '开始年份', width: 100 },
  164. { key: 'endYear', name: '结束年份', width: 100 },
  165. { key: 'dutyUnit', name: '责任单位', width: 100 },
  166. { key: 'comRate', name: '完成情况填写比例(%)', width: 100 },
  167. { key: 'action', name: '操作', width: isAdminAuth.value ? 220 : 100, fixed:'right', align: 'center' },
  168. ]
  169. //表格被选择
  170. const tableCheckKeys = ref([])
  171. const tableCheckChange = (rows) => {
  172. tableCheckKeys.value = rows
  173. emit('check', rows)
  174. }
  175. //项目名称被点击
  176. const rowNameClick = (row) => {
  177. emit('tap', row)
  178. }
  179. //抽屉面板详情
  180. const rowDrawerInfo = ref({})
  181. //工作完成情况
  182. const completion = (row) => {
  183. rowDrawerInfo.value = row
  184. isDrawerType.value = 'edit'
  185. isDrawer.value = true
  186. getDetailData(row.id)
  187. }
  188. //保存工作完成情况
  189. const saveCompletionLoading = ref(false)
  190. const saveCompletionClick = async () => {
  191. saveCompletionLoading.value = true
  192. const form = deepClone(yearData.value)
  193. const { error, code, msg } = await mainApi.workFocusSubmit(form)
  194. saveCompletionLoading.value = false
  195. if (!error && code === 200) {
  196. window?.$message?.success(msg)
  197. emit('change')
  198. } else {
  199. window.$message.error(msg ?? '保存失败')
  200. }
  201. }
  202. //查看
  203. const isDrawer = ref(false)
  204. const isDrawerType = ref('view')
  205. const examine = (row) => {
  206. rowDrawerInfo.value = row
  207. isDrawerType.value = 'view'
  208. isDrawer.value = true
  209. getDetailData(row.id)
  210. }
  211. //数据详情
  212. const yearKey = ref('2023')
  213. const yearIndex = ref(-1)
  214. const yearChange = (_, index) => {
  215. yearIndex.value = index
  216. }
  217. //获取数据详情
  218. const yearData = ref({})
  219. const getDetailData = async (id) => {
  220. const { error, code, data, msg } = await mainApi.queryWorkFocusProgressInfoById(id)
  221. if (error || code !== 200) {
  222. window.$message.error(msg ?? '获取数据失败')
  223. drawerCancel()
  224. return
  225. }
  226. //处理数据
  227. const res = getObjValue(data)
  228. const table = getArrValue(res.table)
  229. res.table = table
  230. yearData.value = res
  231. //设置默认数据
  232. if (table.length > 0) {
  233. let isIndex = false
  234. for (let i = 0; i < table.length; i++) {
  235. const planYear = table[i].year
  236. if (planYear === currentYear) {
  237. yearIndex.value = i
  238. yearKey.value = table[i].year
  239. isIndex = true
  240. }
  241. }
  242. //默认值
  243. if (!isIndex) {
  244. yearIndex.value = 0
  245. yearKey.value = table[0].year
  246. }
  247. } else {
  248. yearIndex.value = -1
  249. yearKey.value = null
  250. }
  251. }
  252. //关闭抽屉
  253. const drawerCancel = () => {
  254. isDrawer.value = false
  255. isDrawerType.value = ''
  256. yearData.value = {}
  257. yearIndex.value = -1
  258. yearKey.value = null
  259. }
  260. //删除
  261. const delTableItem = async ({ item }, resolve) => {
  262. const { error, code, msg } = await mainApi.del(item.id)
  263. if (!error && code === 200) {
  264. window.$message.success('删除成功')
  265. resolve()
  266. emit('change')
  267. } else {
  268. window.$message.error(msg ?? '删除失败')
  269. }
  270. }
  271. //导出数据
  272. const deriveTableItem = async ({ item }, resolve) => {
  273. const { error, val } = await mainApi.exportWorkfocus(item.id)
  274. if (error) {
  275. window.$message?.error('数据异常')
  276. resolve()
  277. return
  278. }
  279. await newDownBlob(val)
  280. resolve()
  281. }
  282. </script>
  283. <style lang="scss">
  284. .hc-project-list-flex {
  285. position: relative;
  286. height: 100%;
  287. display: flex;
  288. flex-direction: column;
  289. .hc-project-list-info-table {
  290. flex-shrink: 0;
  291. }
  292. .hc-project-list-drawer-year {
  293. position: relative;
  294. border: 1px solid #dddddd;
  295. border-top: 0;
  296. flex: 1;
  297. flex-basis: auto;
  298. .el-table {
  299. --el-table-border-color: #dcdcdc;
  300. --el-table-header-text-color: #101010;
  301. --el-table-row-hover-bg-color: transparent;
  302. }
  303. .el-table th.el-table__cell.line {
  304. padding: 0;
  305. height: 100%;
  306. .cell {
  307. padding: 0;
  308. height: 100%;
  309. display: contents;
  310. }
  311. .hc-table-th-line {
  312. position: relative;
  313. height: 100%;
  314. font-size: 14px;
  315. display: contents;
  316. .left {
  317. position: absolute;
  318. bottom: 2px;
  319. left: 10px;
  320. }
  321. .right {
  322. position: absolute;
  323. top: 2px;
  324. right: 2px;
  325. }
  326. &::after {
  327. content: '';
  328. position: absolute;
  329. top: 23px;
  330. left: -20px;
  331. width: 180px;
  332. height: 1px;
  333. background: #dcdcdc;
  334. transform: rotate(17.5deg);
  335. }
  336. }
  337. }
  338. &.edit .el-table--enable-row-transition .el-table__body td.el-table__cell {
  339. height: 60px;
  340. }
  341. .el-table .el-table__cell .cell {
  342. .hc-new-main-body_content {
  343. padding: 4px !important;
  344. .el-input, .el-textarea {
  345. height: 100%;
  346. .el-input__inner {
  347. text-align: center;
  348. }
  349. .el-textarea__inner {
  350. height: 100%;
  351. }
  352. }
  353. }
  354. }
  355. }
  356. }
  357. </style>