index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. <template>
  2. <div class="app-container">
  3. <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
  4. <el-form-item label="项目编号" prop="projectPid">
  5. <el-input v-model="queryParams.projectPid" placeholder="请输入项目编号" clearable @keyup.enter.native="handleQuery" />
  6. </el-form-item>
  7. <el-form-item label="项目名称" prop="projectName">
  8. <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter.native="handleQuery" />
  9. </el-form-item>
  10. <!-- <el-form-item label="应用名称" prop="appId">
  11. <el-select v-model="queryParams.appId">
  12. <el-option
  13. v-for="item in appList"
  14. :key="item.appId"
  15. :label="item.name"
  16. :value="item.appId"
  17. />
  18. </el-select>
  19. </el-form-item> -->
  20. <el-form-item>
  21. <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
  22. <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
  23. </el-form-item>
  24. </el-form>
  25. <el-row :gutter="10" class="mb8">
  26. <el-col :span="1.5">
  27. <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
  28. v-hasPermi="['system:project:add']">新增</el-button>
  29. </el-col>
  30. <el-col :span="1.5">
  31. <el-button type="success" plain icon="el-icon-edit" size="mini" :disabled="single" @click="handleUpdate"
  32. v-hasPermi="['system:project:edit']">修改</el-button>
  33. </el-col>
  34. <!-- <el-col :span="1.5">
  35. <el-button
  36. type="danger"
  37. plain
  38. icon="el-icon-delete"
  39. size="mini"
  40. :disabled="multiple"
  41. @click="handleDelete"
  42. v-hasPermi="['system:project:remove']"
  43. >删除</el-button>
  44. </el-col> -->
  45. <el-col :span="1.5">
  46. <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
  47. v-hasPermi="['system:project:export']">导出</el-button>
  48. </el-col>
  49. <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
  50. </el-row>
  51. <el-table v-loading="loading" :data="projectList" @selection-change="handleSelectionChange">
  52. <el-table-column type="selection" width="55" align="center" />
  53. <el-table-column label="项目来源" width="90" align="center" prop="sourceFromName" />
  54. <el-table-column label="项目编号" align="center" prop="projectPid" min-width="90">
  55. <template slot-scope="scope">
  56. <el-tooltip placement="top" popper-class="pid-tooltip">
  57. <div slot="content" class="pid-tooltip-content">{{ scope.row.projectPid }}</div>
  58. <span class="pid-ellipsis">{{ scope.row.projectPid }}</span>
  59. </el-tooltip>
  60. </template>
  61. </el-table-column>
  62. <el-table-column label="项目名称" align="center" prop="projectName" min-width="320">
  63. <template slot-scope="scope">
  64. <el-tooltip placement="top" popper-class="pid-tooltip">
  65. <div slot="content" class="pid-tooltip-content">{{ scope.row.projectName }}</div>
  66. <span class="name-ellipsis">{{ scope.row.projectName }}</span>
  67. </el-tooltip>
  68. </template>
  69. </el-table-column>
  70. <!-- <el-table-column label="应用名称" align="center" prop="name" /> -->
  71. <el-table-column label="应用名称" align="center" min-width="360">
  72. <template slot-scope="scope">
  73. <div class="app-tags">
  74. <el-tag
  75. v-for="item in (scope.row.name || [])"
  76. :key="item"
  77. size="mini"
  78. effect="plain"
  79. :class="['app-tag', getTagColorClass(item)]"
  80. disable-transitions
  81. >{{ item }}</el-tag>
  82. </div>
  83. </template>
  84. </el-table-column>
  85. <el-table-column label="项目创建时间" width="160" align="center" prop="createTime" />
  86. <el-table-column label="操作" width="180" align="center" class-name="small-padding fixed-width">
  87. <template slot-scope="scope">
  88. <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
  89. v-hasPermi="['system:project:edit']">修改</el-button>
  90. <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
  91. v-hasPermi="['system:project:remove']">删除</el-button>
  92. </template>
  93. </el-table-column>
  94. </el-table>
  95. <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
  96. @pagination="getList" />
  97. <!-- 添加或修改项目对话框 -->
  98. <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
  99. <el-form ref="form" :model="form" :rules="rules" label-width="80px">
  100. <!-- <el-form-item label="项目编号" prop="projectPid">
  101. <el-input v-model="form.projectPid" placeholder="请输入项目编号" />
  102. </el-form-item> -->
  103. <el-form-item label="项目名称" prop="projectName">
  104. <el-input v-model="form.projectName" placeholder="请输入项目名称" />
  105. </el-form-item>
  106. <!-- <el-form-item label="类型" prop="projectType">
  107. <el-select v-model="form.projectType" clearable>
  108. <el-option v-for="item in projectTypeList" :key="item.dictCode" :label="item.dictLabel"
  109. :value="item.dictCode" />
  110. </el-select>
  111. </el-form-item> -->
  112. <el-form-item label="应用名称" prop="appId">
  113. <el-select v-model="form.appId" multiple style="width: 100%;">
  114. <el-option v-for="item in appList" :key="item.appId" :label="item.name" :value="item.appId" />
  115. </el-select>
  116. </el-form-item>
  117. </el-form>
  118. <div slot="footer" class="dialog-footer">
  119. <el-button type="primary" @click="submitForm">确 定</el-button>
  120. <el-button @click="cancel">取 消</el-button>
  121. </div>
  122. </el-dialog>
  123. </div>
  124. </template>
  125. <script>
  126. import { listProject, getProject, delProject, addProject, updateProject, appList } from "@/api/system/project";
  127. import { getDicts } from "@/api/system/dict/data";
  128. import { getInfo } from "@/api/login";
  129. export default {
  130. name: "Project",
  131. data() {
  132. return {
  133. // 遮罩层
  134. loading: true,
  135. // 选中数组
  136. ids: [],
  137. // 非单个禁用
  138. single: true,
  139. // 非多个禁用
  140. multiple: true,
  141. // 显示搜索条件
  142. showSearch: true,
  143. // 总条数
  144. total: 0,
  145. // 项目表格数据
  146. projectList: [],
  147. // 弹出层标题
  148. title: "",
  149. // 是否显示弹出层
  150. open: false,
  151. // 查询参数
  152. queryParams: {
  153. pageNum: 1,
  154. pageSize: 10,
  155. projectPid: null,
  156. projectName: null,
  157. appId: null,
  158. },
  159. appList: [],
  160. projectTypeList: [],
  161. // 表单参数
  162. form: {},
  163. // 表单校验
  164. rules: {
  165. projectName: [
  166. { required: true, message: '项目名称不能为空', trigger: 'red' }
  167. ],
  168. appId: [
  169. { required: true, message: '应用名称不能为空', trigger: 'red' }
  170. ]
  171. }
  172. };
  173. },
  174. created() {
  175. this.getList();
  176. this.getAppList();
  177. this.getProjectTypeList();
  178. },
  179. methods: {
  180. /** 查询项目列表 */
  181. getList() {
  182. this.loading = true;
  183. listProject(this.queryParams).then(response => {
  184. this.projectList = response.rows;
  185. this.total = response.total;
  186. this.loading = false;
  187. });
  188. },
  189. getProjectTypeList() {
  190. getDicts('project_type').then(response => {
  191. this.projectTypeList = response.data;
  192. });
  193. },
  194. // 取消按钮
  195. cancel() {
  196. this.open = false;
  197. this.reset();
  198. },
  199. // 表单重置
  200. reset() {
  201. this.form = {
  202. projectId: null,
  203. projectPid: null,
  204. projectName: null,
  205. createBy: null,
  206. createTime: null,
  207. updateBy: null,
  208. updateTime: null,
  209. name: null
  210. };
  211. this.resetForm("form");
  212. },
  213. /** 搜索按钮操作 */
  214. handleQuery() {
  215. this.queryParams.pageNum = 1;
  216. this.getList();
  217. },
  218. /** 重置按钮操作 */
  219. resetQuery() {
  220. this.resetForm("queryForm");
  221. this.handleQuery();
  222. },
  223. // 多选框选中数据
  224. handleSelectionChange(selection) {
  225. this.ids = selection.map(item => item.projectId)
  226. this.single = selection.length !== 1
  227. this.multiple = !selection.length
  228. },
  229. /** 新增按钮操作 */
  230. handleAdd() {
  231. this.reset();
  232. this.getAppList();
  233. this.open = true;
  234. this.title = "添加项目";
  235. },
  236. /** 修改按钮操作 */
  237. handleUpdate(row) {
  238. this.reset();
  239. const projectId = row.projectId || this.ids
  240. getProject(projectId).then(response => {
  241. this.form = response.data;
  242. this.open = true;
  243. this.title = "修改项目";
  244. });
  245. },
  246. /** 提交按钮 */
  247. submitForm() {
  248. this.$refs["form"].validate(valid => {
  249. if (valid) {
  250. if (this.form.projectId != null) {
  251. updateProject(this.form).then(response => {
  252. this.$modal.msgSuccess("修改成功");
  253. this.open = false;
  254. this.getList();
  255. });
  256. } else {
  257. addProject(this.form).then(response => {
  258. this.$modal.msgSuccess("新增成功");
  259. this.open = false;
  260. this.getList();
  261. });
  262. }
  263. }
  264. });
  265. },
  266. /** 删除按钮操作 */
  267. handleDelete(row) {
  268. const projectIds = row.projectId || this.ids;
  269. this.$modal.confirm('是否确认删除项目编号为"' + projectIds + '"的数据项?').then(function () {
  270. return delProject(projectIds);
  271. }).then(() => {
  272. this.getList();
  273. this.$modal.msgSuccess("删除成功");
  274. }).catch(() => { });
  275. },
  276. /** 导出按钮操作 */
  277. handleExport() {
  278. this.download('system/project/export', {
  279. ...this.queryParams
  280. }, `project_${new Date().getTime()}.xlsx`)
  281. },
  282. /** 获取应用列表 */
  283. getAppList() {
  284. getInfo().then(res => {
  285. // 获取登录用户信息
  286. const user = res.user
  287. appList(user.userId).then(response => {
  288. this.appList = response.data;
  289. });
  290. }).catch(error => {
  291. reject(error)
  292. })
  293. },
  294. // 根据名称生成稳定的颜色 class
  295. getTagColorClass(name) {
  296. if (!name) return 'tag-color-0'
  297. let hash = 0
  298. for (let i = 0; i < name.length; i++) {
  299. hash = ((hash << 5) - hash) + name.charCodeAt(i)
  300. hash |= 0
  301. }
  302. const idx = Math.abs(hash) % 6
  303. return `tag-color-${idx}`
  304. },
  305. }
  306. };
  307. </script>
  308. <style>
  309. .pid-ellipsis {
  310. display: inline-block;
  311. max-width: 100%;
  312. overflow: hidden;
  313. text-overflow: ellipsis;
  314. white-space: nowrap;
  315. vertical-align: middle;
  316. }
  317. /* Project name ellipsis behavior consistent with pid */
  318. .name-ellipsis {
  319. display: inline-block;
  320. max-width: 100%;
  321. overflow: hidden;
  322. text-overflow: ellipsis;
  323. white-space: nowrap;
  324. vertical-align: middle;
  325. }
  326. /* App names single-line ellipsis inside flexible column */
  327. .app-ellipsis {
  328. display: inline;
  329. max-width: 100%;
  330. white-space: normal;
  331. word-break: break-all;
  332. overflow: visible;
  333. text-overflow: clip;
  334. vertical-align: middle;
  335. }
  336. /* App name tags container: wrap to next line automatically */
  337. .app-tags {
  338. display: flex;
  339. flex-wrap: wrap;
  340. gap: 4px 6px;
  341. justify-content: center;
  342. }
  343. .app-tag {
  344. margin: 0; /* use gap instead */
  345. }
  346. /* Tag color variants */
  347. .app-tag.tag-color-0 {
  348. color: #409EFF;
  349. background-color: rgba(64, 158, 255, 0.08);
  350. border-color: rgba(64, 158, 255, 0.35);
  351. }
  352. .app-tag.tag-color-1 {
  353. color: #67C23A;
  354. background-color: rgba(103, 194, 58, 0.08);
  355. border-color: rgba(103, 194, 58, 0.35);
  356. }
  357. .app-tag.tag-color-2 {
  358. color: #E6A23C;
  359. background-color: rgba(230, 162, 60, 0.08);
  360. border-color: rgba(230, 162, 60, 0.35);
  361. }
  362. .app-tag.tag-color-3 {
  363. color: #F56C6C;
  364. background-color: rgba(245, 108, 108, 0.08);
  365. border-color: rgba(245, 108, 108, 0.35);
  366. }
  367. .app-tag.tag-color-4 {
  368. color: #8A5CF6;
  369. background-color: rgba(138, 92, 246, 0.08);
  370. border-color: rgba(138, 92, 246, 0.35);
  371. }
  372. .app-tag.tag-color-5 {
  373. color: #13C2C2;
  374. background-color: rgba(19, 194, 194, 0.08);
  375. border-color: rgba(19, 194, 194, 0.35);
  376. }
  377. /* Tooltip content selectable and readable */
  378. .pid-tooltip {
  379. max-width: 640px;
  380. user-select: text;
  381. word-break: break-all;
  382. }
  383. .pid-tooltip .pid-tooltip-content {
  384. font-family: Menlo, Monaco, Consolas, 'Courier New', monospace;
  385. font-size: 12px;
  386. line-height: 1.5;
  387. }
  388. </style>