index.vue 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <script setup>
  2. import { onShow } from "@dcloudio/uni-app";
  3. import { nextTick, reactive, ref } from "vue";
  4. import dayjs from "dayjs";
  5. import { getRuiHenTaskList } from "@/api/ruihen";
  6. // 搜索关键词与分页实例
  7. const searchKey = ref("");
  8. const paging = ref(null);
  9. const dataList = ref([]);
  10. // 搜索框样式
  11. const placeholderStyle = ref("color:#797979;font-weight:500;font-size:16px");
  12. const inputStyles = reactive({
  13. backgroundColor: "#FFFFFF",
  14. color: "#797979",
  15. });
  16. // 列表查询:搜索条件统一走 searchKey
  17. const queryList = (pageNo, pageSize) => {
  18. getRuiHenTaskList({
  19. pageNo,
  20. pageSize,
  21. searchKey: searchKey.value,
  22. })
  23. .then((res) => {
  24. paging.value.complete(res.data?.list || []);
  25. })
  26. .catch(() => {
  27. paging.value.complete(false);
  28. });
  29. };
  30. // 触发 z-paging 重新加载
  31. const searchList = () => {
  32. paging.value?.reload();
  33. };
  34. // 跳转新增页
  35. const handleCreateTask = () => {
  36. uni.navigateTo({
  37. url: "/pages/ruihen-task/create",
  38. });
  39. };
  40. // 跳转详情页
  41. const handleView = (item) => {
  42. uni.navigateTo({
  43. url: "/pages/ruihen-task/detail?id=" + item.id,
  44. });
  45. };
  46. // 跳转编辑页
  47. const handleEdit = (item) => {
  48. uni.navigateTo({
  49. url: "/pages/ruihen-task/edit?id=" + item.id,
  50. });
  51. };
  52. // 同时兼容秒级和毫秒级时间戳
  53. const normalizeTimestamp = (time) => {
  54. if (!time) return null;
  55. const value = Number(time);
  56. if (Number.isNaN(value)) return time;
  57. return value < 1000000000000 ? value * 1000 : value;
  58. };
  59. // 创建时间格式化
  60. const formatTime = (time) => {
  61. const value = normalizeTimestamp(time);
  62. return value ? dayjs(value).format("YYYY-MM-DD HH:mm:ss") : "——";
  63. };
  64. // 施工队伍兼容数组和字符串两种返回结构
  65. const formatDeptNames = (deptNames) => {
  66. if (Array.isArray(deptNames)) {
  67. return deptNames.filter(Boolean).join("、") || "——";
  68. }
  69. return deptNames || "——";
  70. };
  71. onShow(() => {
  72. nextTick(() => {
  73. searchList();
  74. });
  75. });
  76. </script>
  77. <template>
  78. <z-paging class="page" ref="paging" v-model="dataList" @query="queryList">
  79. <template #top>
  80. <view class="top">
  81. <!-- 搜索区 -->
  82. <view class="search-row">
  83. <uni-easyinput
  84. v-model="searchKey"
  85. :styles="inputStyles"
  86. :placeholderStyle="placeholderStyle"
  87. :placeholder="$t('operation.searchText')"
  88. @confirm="searchList" />
  89. <button
  90. class="mini-btn"
  91. type="primary"
  92. size="mini"
  93. @click="searchList">
  94. {{ $t("operation.search") }}
  95. </button>
  96. </view>
  97. <!-- 查询区下方新增按钮 -->
  98. <button
  99. class="create-btn"
  100. style="width: 100%"
  101. type="primary"
  102. size="mini"
  103. @click="handleCreateTask">
  104. 新增任务
  105. </button>
  106. </view>
  107. </template>
  108. <view class="list">
  109. <!-- 任务卡片列表 -->
  110. <view
  111. class="item"
  112. v-for="(item, index) in dataList"
  113. :key="item.id || index">
  114. <view class="header">
  115. <!-- 右上角状态固定统一蓝色,仅展示 statusLabel -->
  116. <span class="contract-name"
  117. >合同名称:{{ item.contractName || "——" }}</span
  118. >
  119. <span class="status-tag">{{ item.statusLabel || "——" }}</span>
  120. </view>
  121. <view class="content">
  122. <view class="content-item">
  123. <span class="label">井号:</span>
  124. <span>{{ item.wellName || "——" }}</span>
  125. </view>
  126. <view class="content-item">
  127. <span class="label">施工地点:</span>
  128. <span>{{ item.location || "——" }}</span>
  129. </view>
  130. <view class="content-item">
  131. <span class="label">施工队伍:</span>
  132. <span>{{ formatDeptNames(item.deptNames) }}</span>
  133. </view>
  134. <view class="content-item">
  135. <span class="label">创建时间:</span>
  136. <span>{{ formatTime(item.createTime) }}</span>
  137. </view>
  138. </view>
  139. <view class="footer">
  140. <!-- 列表操作按钮 -->
  141. <button
  142. class="button"
  143. size="mini"
  144. type="primary"
  145. plain="true"
  146. @click="handleView(item)">
  147. {{ $t("operation.view") }}
  148. </button>
  149. <button
  150. class="button"
  151. size="mini"
  152. type="primary"
  153. @click="handleEdit(item)">
  154. {{ $t("operation.edit") }}
  155. </button>
  156. </view>
  157. </view>
  158. </view>
  159. </z-paging>
  160. </template>
  161. <style scoped>
  162. .page {
  163. padding: 10px;
  164. }
  165. .top {
  166. display: flex;
  167. flex-direction: column;
  168. gap: 12px;
  169. background: #f3f5f9;
  170. padding: 10px 0;
  171. }
  172. .search-row {
  173. display: flex;
  174. align-items: center;
  175. gap: 12px;
  176. }
  177. .search-row :deep(.uni-easyinput) {
  178. flex: 1;
  179. }
  180. :deep(.mini-btn) {
  181. height: 38px !important;
  182. font-size: 16px !important;
  183. margin: 0;
  184. }
  185. .create-btn {
  186. margin: 0;
  187. align-self: flex-start;
  188. }
  189. .list {
  190. margin-top: 16px;
  191. display: flex;
  192. flex-direction: column;
  193. gap: 12px;
  194. }
  195. .item {
  196. background-color: #fff;
  197. padding: 12px;
  198. border-radius: 8px;
  199. display: flex;
  200. flex-direction: column;
  201. gap: 12px;
  202. }
  203. .header {
  204. display: flex;
  205. align-items: flex-start;
  206. justify-content: space-between;
  207. gap: 12px;
  208. }
  209. .contract-name {
  210. flex: 1;
  211. font-size: 16px;
  212. font-weight: 600;
  213. color: #1f2329;
  214. word-break: break-all;
  215. }
  216. .status-tag {
  217. flex-shrink: 0;
  218. display: inline-flex;
  219. align-items: center;
  220. padding: 5px 10px;
  221. border-radius: 4px;
  222. font-size: 14px;
  223. font-weight: 500;
  224. color: #1677ff;
  225. background: rgba(22, 119, 255, 0.12);
  226. }
  227. .content {
  228. display: flex;
  229. flex-direction: column;
  230. gap: 8px;
  231. }
  232. .content-item {
  233. font-size: 14px;
  234. font-weight: 400;
  235. color: #4e5969;
  236. word-break: break-all;
  237. }
  238. .label {
  239. display: inline-block;
  240. width: 76px;
  241. font-weight: 500;
  242. color: #1f2329;
  243. }
  244. .footer {
  245. display: flex;
  246. justify-content: flex-end;
  247. align-items: center;
  248. gap: 12px;
  249. }
  250. .button {
  251. margin: 0;
  252. }
  253. </style>