Przeglądaj źródła

1.0.0 去除bootstrap,使用antd重构

liuyanpeng 1 rok temu
rodzic
commit
ecb0008e39

+ 1 - 1
README.md

@@ -1,4 +1,4 @@
-# client-trace-v3
+# client-trace-v5
 
 ### 项目安装
 ```

+ 5 - 5
package.json

@@ -1,7 +1,7 @@
 {
-  "name": "client-trace-v3",
-  "description": "client-trace-v3",
-  "version": "1.0.28",
+  "name": "client-trace-v5",
+  "description": "client-trace-v5",
+  "version": "1.0.0",
   "author": "yangzhijie <yangzhijie1488@163.com>",
   "scripts": {
     "dev": "webpack serve --config ./webpack.dev.js",
@@ -15,7 +15,7 @@
   ],
   "peerDependencies": {
     "ant-design-vue": "^4.2.1",
-    "pc-component-v3": "^1.1.12",
+    "pc-component-v3": "^2.0.0",
     "v-tooltip": "^4.0.0-beta.17",
     "vue-select": "^4.0.0-beta.6",
     "vuedraggable": "^4.1.0"
@@ -51,6 +51,6 @@
   },
   "repository": {
     "type": "http",
-    "url": "http://a.leanwo.com:3000/prodog-client-2023/client-eam-v3.git"
+    "url": "https://a.leanwo.com:3000/prodog-client-2025/client-trace-v5.git"
   }
 }

+ 16 - 14
public/index-debug.html

@@ -6,26 +6,28 @@
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
   <title>client-trace</title>
 
-	<link nonce="*NONCE_TOKEN_CSS*" rel="stylesheet" type="text/css" href="/static/bootstrap/dist/css/bootstrap.css">
-	<link nonce="*NONCE_TOKEN_CSS*" rel="stylesheet" type="text/css" href="/static/bootstrap/dist/css//bootstrap-theme.min.css">
-	<link nonce="*NONCE_TOKEN_CSS*" rel="stylesheet" type="text/css" href="/static/bootstrap3-dialog/dist/css/bootstrap-dialog.css">
+    <link rel="stylesheet" type="text/css" href="/static/dhtmlxgantt/dhtmlxgantt.css">
   
-  <link rel="stylesheet" type="text/css" href="/static/bootstrap-treeview/bootstrap-treeview.css">
-  <link rel="stylesheet" type="text/css" href="/static/summernote/dist/summernote.min.css">
-
+	<link nonce="*NONCE_TOKEN_CSS*" rel="stylesheet" type="text/css" href="/static/trumbowyg/css/trumbowyg.min.css">
 
 	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/jquery/dist/jquery.js"></script>
 
-
-	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/bootstrap/dist/js/bootstrap.js"></script>
-	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/bootstrap3-dialog/dist/js/bootstrap-dialog.js"></script>
-  
-	<script src="/static/bootstrap-treeview/bootstrap-treeview.js"></script>
-	<script src="/static/summernote/dist/summernote.min.js"></script>
-	<script src="/static/summernote/dist/lang/summernote-zh-CN.min.js"></script>
+	
+	<!-- 富文本编辑器 -->
+	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/trumbowyg/js/trumbowyg.min.js"></script> 
+	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/trumbowyg/js/trumbowyg.upload.min.js"></script>
+	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/trumbowyg/js/trumbowyg.zh_cn.min.js"></script>
+	
 	<script src="/static/dayjs/dayjs.min.js"></script>
 
-  
+	<script src="/static/dhtmlxgantt/dhtmlxgantt.js"></script>
+	<script src="/static/dhtmlxgantt/locale/locale_cn.js"></script>
+	<script src="/static/dhtmlxgantt/ext/dhtmlxgantt_auto_scheduling.js"></script>
+	<script src="/static/dhtmlxgantt/ext/dhtmlxgantt_smart_rendering.js"></script>
+	<script src="/static/dhtmlxgantt/ext/dhtmlxgantt_undo.js"></script>
+	<script src="/static/dhtmlxgantt/ext/dhtmlxgantt_fullscreen.js"></script>
+	<script src="/static/dhtmlxgantt/ext/api.js"></script>
+
 	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/vue/dist/vue.global.js"></script>
 	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/vue-i18n/dist/vue-i18n.global.js"></script>
 	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/vuex/dist/vuex.global.js"></script>

+ 6 - 13
public/index-release.html

@@ -6,24 +6,17 @@
   <meta name="viewport" content="width=device-width,initial-scale=1.0">
   <title>client-trace</title>
 
-	<link nonce="*NONCE_TOKEN_CSS*" rel="stylesheet" type="text/css" href="/static/bootstrap/dist/css/bootstrap.min.css">
-	<link nonce="*NONCE_TOKEN_CSS*" rel="stylesheet" type="text/css" href="/static/bootstrap/dist/css//bootstrap-theme.min.css">
-	<link nonce="*NONCE_TOKEN_CSS*" rel="stylesheet" type="text/css" href="/static/bootstrap3-dialog/dist/css/bootstrap-dialog.min.css">
-  
-  <link rel="stylesheet" type="text/css" href="/static/bootstrap-treeview/bootstrap-treeview.css">
-  <link rel="stylesheet" type="text/css" href="/static/summernote/dist/summernote.min.css">
+	<link nonce="*NONCE_TOKEN_CSS*" rel="stylesheet" type="text/css" href="/static/trumbowyg/css/trumbowyg.min.css">
 
   <link rel="stylesheet" type="text/css" href="/static/dhtmlxgantt/dhtmlxgantt.css">
 
 	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/jquery/dist/jquery.min.js"></script>
-
-
-	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/bootstrap/dist/js/bootstrap.js"></script>
-	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/bootstrap3-dialog/dist/js/bootstrap-dialog.js"></script>
   
-  <script src="/static/bootstrap-treeview/bootstrap-treeview.js"></script>
-  <script src="/static/summernote/dist/summernote.min.js"></script>
-  <script src="/static/summernote/dist/lang/summernote-zh-CN.min.js"></script>
+	<!-- 富文本编辑器 -->
+	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/trumbowyg/js/trumbowyg.min.js"></script> 
+	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/trumbowyg/js/trumbowyg.upload.min.js"></script>
+	<script nonce="*NONCE_TOKEN*" type="text/javascript" src="/static/trumbowyg/js/trumbowyg.zh_cn.min.js"></script>
+
 	<script src="/static/dayjs/dayjs.min.js"></script>
 
   <script src="/static/dhtmlxgantt/dhtmlxgantt.js"></script>

+ 24 - 5
src/App.vue

@@ -15,14 +15,33 @@ import 'dayjs/locale/zh-cn';
 dayjs.locale('zh-cn');
 export default {
   name: 'App',
-  data:function(){
+  data: function () {
     return {
-      locale:zhCN,
+      locale: zhCN,
     };
   },
 };
-</script>
 
-<style>
+const debounce = (fn, delay) => {
+  let timer;
+  return (...args) => {
+    if (timer) {
+      clearTimeout(timer);
+    }
+    timer = setTimeout(() => {
+      fn(...args);
+    }, delay);
+  };
+};
+
+// 消除 ResizeObserver loop 报错
+const _ResizeObserver = window.ResizeObserver;
+window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
+  constructor(callback) {
+    callback = debounce(callback, 200);
+    super(callback);
+  }
+};
+</script>
 
-</style>
+<style></style>

+ 33 - 2
src/common/Common.js

@@ -1,4 +1,4 @@
-import { Notify } from 'pc-component-v3';
+import { Notify, DownloadService } from 'pc-component-v3';
 
 
 export default{
@@ -194,7 +194,22 @@ export default{
 
   },
 
-
+  // 后台接口返回的数据带className使用
+  getResourceUrl1: function (type, resourceName) {
+    var accountId = localStorage.getItem('#accountId');
+    if (resourceName == undefined  || type == undefined || resourceName == ''  || type == '') {
+      return;
+    }
+    if (type == 'image') {
+      return window.location.origin + '/Files/' + accountId + '/Images/' + resourceName;
+    }
+    if (type == 'video') {
+      return window.location.origin + '/Files/' + accountId + '/Video/' + resourceName;
+    }
+    if (type == 'file') {
+      return window.location.origin + '/Files/' + accountId + '/Files/' + resourceName;
+    }
+  },
 
 
 
@@ -231,6 +246,22 @@ export default{
     }
   },
 
+  // trumbowyg 查看内容时通过对应的a标签进行附件下载
+  downloadByA: function (element) {
+    const contents = document.querySelectorAll(element);
+    if (!contents.length) return;
+    contents.forEach(content => {
+      content.addEventListener('click', function (e) {
+        const target = e.target;
+        if (target && target.classList && target.classList.contains('trumbowyg-attachment-link')) {
+          e.preventDefault();
+          const fileName = target.getAttribute('file-name');
+          const className = target.getAttribute('class-name');
+          DownloadService.fileDownload(className, fileName);
+        }
+      });
+    });
+  },
 
 };
 

+ 96 - 0
src/common/utils.js

@@ -0,0 +1,96 @@
+
+
+import Common from './Common';
+
+export const ajaxApiGet = (url, datas) => {
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: Common.getApiURL(url),
+      type: 'get',
+      contentType: 'application/json',
+      beforeSend: function (request) {
+        Common.addTokenToRequest(request);
+      },
+
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+
+export const ajaxApiPost = (url, datas) => {
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: Common.getApiURL(url),
+      type: 'post',
+      contentType: 'application/json',
+      data: JSON.stringify(datas),
+      beforeSend: function (request) {
+        Common.addTokenToRequest(request);
+      },
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+
+
+export const ajaxApiFile = (url, datas) => {
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: Common.getApiURL(url),
+      type: 'post',
+      data: datas,
+      processData: false,
+      contentType: false,
+      beforeSend: function (request) {
+        Common.addTokenToRequest(request);
+      },
+
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+
+// 下载文件
+export const download = url => {
+  return new Promise((resolve, reject) => {
+    $.ajax({
+      url: url,
+      method: 'GET',
+
+      xhrFields: {
+        responseType: 'blob',
+      },
+      success: function (data) {
+        resolve(data);
+      },
+      error: function (XMLHttpRequest, textStatus, errorThrown) {
+        reject(XMLHttpRequest);
+      },
+    });
+  });
+};
+
+export const debounce = (fn, delay = 500) => {
+  let timer = null;
+  return function (...args) {
+    clearTimeout(timer);
+    timer = setTimeout(() => {
+      fn.apply(this, args);
+    }, delay);
+  };
+};

+ 60 - 38
src/trace/CompleteProjectList.vue

@@ -2,51 +2,62 @@
   <div>
     <TraceHeader :type="'traceProject'" />
     <div>
-      <div>
+      <a-space direction="vertical" style="width: 100%">
         <QueryWidget
           ref="queryWidget"
-          @search="getDatas()"
-          @value-changed="getDatas()"
+          @search="query()"
+          @value-changed="query()"
         />
-        <p
+
+        <a-alert
           v-if="projectList == null || projectList.length == 0"
-          class="bg-danger"
-          style="padding: 15px; margin-top: 15px"
-        >
-          您参与的项目都在进行中
-        </p>
-        <div class="row" style="margin-top: 10px">
-          <div v-for="item in projectList" :key="item.id" class="col-md-4">
-            <div class="panel panel-default" @click="openLine(item)">
-              <div class="panel-body" style="height: 70px">
-                <div>
-                  <div>{{ item.name }}</div>
-                  <div>
-                    <span>{{ item.no }}</span>
-                    <span v-if="item.admin" style="color: green"> 参与中 </span>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
+          message="您参与的项目都在进行中"
+          type="warning"
+          style="margin-top: 16px"
+        />
+
+        <a-row v-else :gutter="[16, 16]">
+          <a-col
+            v-for="item in projectList"
+            :key="item.id"
+            :xs="24"
+            :sm="12"
+            :md="8"
+            :lg="8"
+            :xl="6"
+          >
+            <a-card hoverable @click="openLine(item)">
+              <a-typography-title :level="5" style="margin: 0">
+                {{ item.name }}
+              </a-typography-title>
+              <a-space>
+                <a-typography-text>{{ item.no }}</a-typography-text>
+                <a-typography-text v-if="item.admin" type="success">
+                  参与中
+                </a-typography-text>
+              </a-space>
+            </a-card>
+          </a-col>
+        </a-row>
 
-        <div class="row">
-          <div class="col-md-12">
-            <VueBootstrapPagination
-              :pagination="pagination"
-              :callback="getDatas"
-            />
-          </div>
+        <div v-show="projectList && projectList.length" style="margin: 16px 0">
+          <AntdPagination
+            ref="paginationRef"
+            :pagination="pagination"
+            @get-page-params="getPageParams"
+          />
+          <a-divider />
         </div>
 
-        <hr />
-        <button type="button" class="btn btn-link" @click="isTheArchive">
-          进行中的项目
-        </button>
-      </div>
 
-      <h3>&nbsp;</h3>
+        <a-button
+          type="link"
+          style="color: green; padding: 0px"
+          @click="isTheArchive"
+        >
+          进行中的项目
+        </a-button>
+      </a-space>
     </div>
   </div>
 </template>
@@ -110,6 +121,17 @@ export default {
     this.getDatas();
   },
   methods: {
+    query: function () {
+      this.$refs.paginationRef.backFirstPage();
+    },
+
+    // 页数改变
+    getPageParams: function (page, pageSize) {
+      this.pagination.current_page = page;
+      this.pagination.per_page = pageSize;
+      this.getDatas(true);
+    },
+
     /**
      * 查询数据
      * @author ZhangTeng 20190220
@@ -232,4 +254,4 @@ p {
   height: 33px;
   width: 38px;
 }
-</style>
+</style>

+ 225 - 165
src/trace/FinishedProjectTraces.vue

@@ -1,105 +1,102 @@
 <template>
   <div>
-    <Navbar
-      :title="projectName"
-      :is-go-back="true"
-    >
-      <button
-        type="button"
-        class="btn btn-link"
-        style="padding: 0px;"
+    <Navbar :title="projectName" :is-go-back="true">
+      <a-button
+        type="link"
+        style="padding: 5px; color: #337ab7"
         @click="goPersonList()"
       >
         编辑人员({{ projectUsers.length }})
-      </button>
+      </a-button>
     </Navbar>
 
-    <h4>
+    <h4 class="operation-title">
       已完成的任务({{ pagination2.total }})
-      <div class="pull-right">
-        <button
-          class="btn btn-link"
-          style="padding: 0px;"
+      <div>
+        <a-button
+          v-if="timeShow"
+          type="link"
+          style="padding: 0px; color: #337ab7"
           @click="projectManagement(projectId)"
         >
           项目任务
-        </button>&nbsp;
-        <button
-          class="btn btn-link"
-          style="padding: 0px;"
+        </a-button>&nbsp;
+        <a-button
+          type="link"
+          style="padding: 0px; color: #337ab7"
           @click="projectArchive(projectId)"
         >
           项目归档
-        </button>&nbsp;
-        <button
-          class="btn btn-link"
-          style="padding: 0px;"
+        </a-button>&nbsp;
+        <a-button
+          type="link"
+          style="padding: 0px; color: #337ab7"
           @click="addTrace(projectId)"
         >
           添加任务
-        </button>
+        </a-button>
       </div>
     </h4>
-    <div class="input-group">
-      <input v-model="content" autocomplete="off" type="text" class="form-control" placeholder="任务名称、任务内容" @blur="listFinishedTask" @keyup.enter="listFinishedTask" />
-      <span class="input-group-btn">
-        <button style="width:100%;background-color: #007aff;color: white;" type="button" class="btn btn-blue search-button" @click="listFinishedTask()">
-          查询
-        </button>
-      </span>
-    </div>
     <div>
-      <div
-        class="panel panel-default"
-        style="margin-bottom: 10px;margin-top: 10px;"
+      <QueryWidget
+        ref="queryWidget"
+        placeholder-text="任务名称、任务内容"
+        @search="query()"
+        @value-changed="query()"
+      />
+    </div>
+
+    <div class="form-group list-box">
+      <vuedraggable
+        v-model="finishedTraces"
+        :disabled="true"
+        item-key="id"
+        class="wrapper"
+        @change="end"
       >
-        <div class="panel-body">
-          <vuedraggable v-model="finishedTraces" :disabled="true" item-key="id" class="wrapper" @change="end">
-            <template #item="{element}">
-              <div style="margin-top: 5px; cursor: pointer;">
-                <span>
-                  <Checkbox
-                    :id="'trace-finish-' + element.id"
-                    v-model:value="element.finished"
-                    class-name="terms"
-                    class="trace-checkbox"
-                    @update:value="updateTracefinished(element)"
-                  />
-                </span>
-                <span @click="openLine(element)">
-                  <!-- eslint-disable-next-line -->
-                    <span class="trace-summary" :class="{'font-color': element.timeLineCompletion==true}" v-html="element.summary" />
-                  <span
-                    class="glyphicon glyphicon-option-vertical trace-icon"
-                    aria-hidden="true"
-                  />
-                  <span class="label label-primary trace-user">
-                    <!-- eslint-disable-next-line -->
-                      <span v-html="element.receiveUserName" />&nbsp;
-                    <span>{{ formatDate(element.planFinishedDate) }}</span>
-                  </span>
-                  <span class="badge">
-                    <span>完成时间:{{ formatDate(element.finishedDate) }}</span>
-                  </span>
+        <template #item="{ element }">
+          <div style="margin-top: 5px; cursor: pointer">
+            <span>
+              <span>
+                <a-checkbox
+                  v-model:checked="element.finished"
+                  @change="updateTracefinished(element)"
+                />
+              </span>
+              <span @click="openLine(element)">
+                <!-- eslint-disable-next-line -->
+                <span
+                  class="trace-summary"
+                  :class="{
+                    'font-color': element.timeLineCompletion == true,
+                  }"
+                >
+                  {{ element.summary }}
                 </span>
-              </div>
-            </template>
-          </vuedraggable>  
-        </div>
-      </div>
-      <div v-if="finishedTraces.length" class="row">
-        <div class="pull-left">
-          <span>第{{ (pagination2.current_page-1)*pagination2.per_page+1 }}-{{ pagination2.current_page*pagination2.per_page }}条,共计{{ pagination2.total }}条,每页显示</span>
-          <PageSizeSelect :size="pagination2.per_page" @page-size-changed="gridSizeSelect" />
-          <span>条</span>
-        </div>
-			
-        <div class="pull-right">
-          <VueBootstrapPagination :pagination="pagination2" :callback="listFinishedTask" @click="updateRouter" />
-        </div>
-      </div>
+                <a-tag
+                  class="label"
+                  :color="element.overdue ? '#d9534f' : '#337ab7'"
+                >
+                  <span>{{ element.receiveUserName }}</span>&nbsp;
+                  <span>{{ formatDate(element.planFinishedDate) }}</span>
+                </a-tag>
+                <a-tag class="label" color="#777">
+                  <span>完成时间:{{ formatDate(element.finishedDate) }}</span>
+                </a-tag>
+              </span>
+            </span>
+          </div>
+        </template>
+      </vuedraggable>
     </div>
   </div>
+  <div style="margin: 16px 0">
+    <AntdPagination
+      ref="paginationRef"
+      :pagination="pagination2"
+      @get-page-params="getPageParams"
+    />
+  </div>
 </template>
 
 <script>
@@ -108,12 +105,13 @@ import Common from '../common/Common.js';
 import TraceCommon from './TraceCommon.js';
 import TraceResource from './TraceResource.js';
 
-
 import vuedraggable from 'vuedraggable';
+import QueryWidget from '../widget/QueryWidget.vue';
 
 export default {
   components: {
     vuedraggable,
+    QueryWidget,
   },
   data: function () {
     return {
@@ -125,15 +123,27 @@ export default {
       projectId: '',
       pagination2: {
         total: 0,
-        per_page: 50,    // 每页10条信息
+        per_page: 50, // 每页10条信息
         current_page: 1, // 当前页码
-        last_page: 10,    // 最后页码
+        last_page: 10, // 最后页码
       },
       formatDate: TraceCommon.formatDate,
-      content:undefined,
+      content: undefined,
     };
   },
 
+  watch: {
+    // 监听路由是否变化
+    $route(to, from) {
+      if (to.query.currentPage != from.query.currentPage ||
+      to.query.pageSize != from.query.pageSize) {
+        this.pagination2.current_page = Number(to.query.currentPage);
+        this.pagination2.per_page = Number(to.query.pageSize);
+        this.listFinishedTask();
+      }
+    },
+  },
+
   mounted: function () {
     var _self = this;
     this.projectId = _self.$route.params.projectId;
@@ -144,12 +154,6 @@ export default {
     this.listFinishedTask();
   },
   methods: {
-    gridSizeSelect: function(newPageSize) {
-      this.pagination2.per_page = newPageSize;
-      this.pagination2.current_page = 1;
-      // 刷新界面
-      this.updateRouter();
-    },
     end(evt) {
       var _self = this;
       $.ajax({
@@ -158,33 +162,45 @@ export default {
         dataType: 'json',
         contentType: 'application/json',
         data: {
-          'id': evt.moved.element.id,
-          'projectId': _self.projectId,
-          'oldIndex': evt.moved.oldIndex,
-          'newIndex': evt.moved.newIndex,
-          'currentPage': _self.pagination2.current_page,
-          'pageSize': _self.pagination2.per_page,
-          'content': _self.content,
+          id: evt.moved.element.id,
+          projectId: _self.projectId,
+          oldIndex: evt.moved.oldIndex,
+          newIndex: evt.moved.newIndex,
+          currentPage: _self.pagination2.current_page,
+          pageSize: _self.pagination2.per_page,
+          content: _self.content,
         },
-        beforeSend: function(request) {
+        beforeSend: function (request) {
           Common.addTokenToRequest(request);
         },
-        success: function(data) {
+        success: function (data) {
           _self.listFinishedTask();
         },
-        error: function(XMLHttpRequest, textStatus, errorThrown) {
+        error: function (XMLHttpRequest, textStatus, errorThrown) {
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
     },
     /**
-         * 编辑
-         */
+     * 编辑
+     */
     edit: function (trace) {
       var _self = this;
       this.$router.push('/trace/traceUpdate/' + trace.id);
     },
 
+    query: function () {
+      this.pagination2.current_page = 1;
+      this.updateRouter();
+    },
+
+    // 页数改变
+    getPageParams: function (page, pageSize) {
+      this.pagination2.current_page = page;
+      this.pagination2.per_page = pageSize;
+      this.updateRouter();
+    },
+
     updateRouter: function () {
       this.$router.push({
         path: '/trace/finishedProjectTraces/' + this.projectId,
@@ -194,37 +210,42 @@ export default {
           pageSize: this.pagination2.per_page,
         },
       });
-
-      console.log(this.$route);
-      // console.log(this.$router.query.currentPage);
-      // console.log(this.$router.query.pageSize);
-      // this.listFinishedTask();
+      if (this.pagination.current_page === 1) {
+        this.listNotFinishedTask();
+      }
     },
 
     /**
-         * 初始化项目
-         */
+     * 初始化项目
+     */
     listFinishedTask: function () {
       var _self = this;
       _self.finishedTraces.splice(0, _self.finishedTraces.length);
-      TraceResource.listFinishedByProjectId(_self.projectId, _self.pagination2, _self.content).then(data => {
-        if (data == null) {
-          return;
-        }
-        _self.pagination2.total = data.totalSize;
-        _self.pagination2.last_page = Math.ceil(_self.pagination2.total / _self.pagination2.per_page);
-        _self.finishedTraces = data.traceDtos;
-      }, error => {
-        Common.processException(error);
-      });
+      TraceResource.listFinishedByProjectId(
+        _self.projectId,
+        _self.pagination2,
+        _self.$refs.queryWidget.getSearchText(),
+      ).then(
+        data => {
+          if (data == null) {
+            return;
+          }
+          _self.pagination2.total = data.totalSize;
+          _self.pagination2.last_page = Math.ceil(
+            _self.pagination2.total / _self.pagination2.per_page,
+          );
+          _self.finishedTraces = data.traceDtos;
+        },
+        error => {
+          Common.processException(error);
+        },
+      );
     },
 
-
-
     /**
-         * 初始化接收该项目的人数
-         * @author ZhangTeng 20190212
-         */
+     * 初始化接收该项目的人数
+     * @author ZhangTeng 20190212
+     */
     personNumber: function () {
       var _self = this;
       _self.projectUsers.splice(0, _self.projectUsers.length);
@@ -235,7 +256,7 @@ export default {
         dataType: 'json',
         contentType: 'application/json',
         data: {
-          'projectId': _self.projectId,
+          projectId: _self.projectId,
         },
         beforeSend: function (request) {
           Common.addTokenToRequest(request);
@@ -256,9 +277,9 @@ export default {
     },
 
     /**
-         * 打开明细
-         * @author ZhangTeng 20190131
-         */
+     * 打开明细
+     * @author ZhangTeng 20190131
+     */
     openLine: function (obj) {
       this.$router.push({
         path: '/trace/trace/' + obj.id,
@@ -266,47 +287,45 @@ export default {
     },
 
     /**
-         * 添加任务
-         * @author ZhangTeng 20190201
-         */
+     * 添加任务
+     * @author ZhangTeng 20190201
+     */
     addTrace: function (projectId) {
       this.$router.push('/trace/traceCreate/' + projectId);
     },
 
     /**
-		 * 项目归档
-		 * @param {Object} projectId
-		 * @author GuoZhiBo 20190926
-		 */
+     * 项目归档
+     * @param {Object} projectId
+     * @author GuoZhiBo 20190926
+     */
     projectArchive: function (projectId) {
       var _self = this;
       this.$router.push({
         path: '/trace/projectArchive/' + projectId,
-        query:
-                {
-                  projectName: _self.projectName,
-                },
+        query: {
+          projectName: _self.projectName,
+        },
       });
     },
     /**
-		 * 项目任务
-		 * @param {Object} projectId
-		 * @author GuoZhiBo 20190926
-		 */
+     * 项目任务
+     * @param {Object} projectId
+     * @author GuoZhiBo 20190926
+     */
     projectManagement: function (projectId) {
       var _self = this;
       this.$router.push({
         path: '/trace/projectManagement/' + projectId,
-        query:
-                    {
-                      projectName: _self.projectName,
-                    },                
+        query: {
+          projectName: _self.projectName,
+        },
       });
     },
     /**
-         *  跳转到人员界面
-         *  @author ZhangTeng 20190212
-         */
+     *  跳转到人员界面
+     *  @author ZhangTeng 20190212
+     */
     goPersonList: function () {
       var _self = this;
       this.$router.push({
@@ -318,39 +337,80 @@ export default {
     },
 
     /**
-         * 修改追踪单的状态
-         * @author YangZhiJie 20171201
-         */
+     * 修改追踪单的状态
+     * @author YangZhiJie 20171201
+     */
     updateTracefinished: function (trace) {
       var _self = this;
-      TraceResource.updateTraceFinished(trace.id).then(successData => {
-        _self.listFinishedTask();
-      }, errorData => {
-        Common.processException(errorData);
-      });
+      TraceResource.updateTraceFinished(trace.id).then(
+        successData => {
+          _self.listFinishedTask();
+        },
+        errorData => {
+          Common.processException(errorData);
+        },
+      );
     },
-
   },
 };
 </script>
 
 <style scoped>
 .trace-summary {
-    font-size: large;
-    margin-left: 5px;
-    margin-right: 5px;
+  font-size: large;
+  margin-left: 5px;
+  margin-right: 5px;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  line-height: 1.42857143;
+  color: #333;
 }
 
 .trace-icon {
-    opacity: 0.5;
+  opacity: 0.5;
 }
 
 .trace-user {
-    font-size: 100%;
+  font-size: 100%;
 }
 
 .trace-checkbox {
-    display: inline;
-    font-size: large;
+  display: inline;
+  font-size: large;
+}
+.label {
+  display: inline;
+  padding: 0.2em 0.6em 0.3em;
+  font-size: 75%;
+  font-weight: 700;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  border-radius: 0.25em;
+}
+
+.operation-title {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin: 4px 8px !important;
+}
+h4 {
+  font-family: inherit;
+  font-weight: 500;
+  line-height: 1.1;
+  color: inherit;
+}
+
+.list-box {
+  margin-top: 10px;
+  border: 1px solid #ddd;
+  padding: 10px 16px;
+  border-radius: 6px;
+}
+
+:deep(.ant-divider-horizontal) {
+  margin: 6px 0 !important;
 }
-</style>
+</style>

+ 112 - 150
src/trace/NotFinishedProjectTraces.vue

@@ -1,164 +1,72 @@
 <template>
   <div>
     <Navbar :title="projectName" :is-go-back="true">
-      <button
-        v-if="adminUserShow"
-        type="button"
-        class="btn btn-link"
-        style="padding: 5px"
-        @click="goAdminPersonList()"
-      >
+      <a-button v-if="adminUserShow" type="link" style="padding: 5px; color: #337ab7" @click="goAdminPersonList()">
         编辑项目管理员({{ projectAdminUsers.length }})
-      </button>
-      <button
-        v-if="userShow"
-        type="button"
-        class="btn btn-link"
-        style="padding: 5px"
-        @click="goPersonList()"
-      >
+      </a-button>
+      <a-button v-if="userShow" type="link" style="padding: 5px; color: #337ab7" @click="goPersonList()">
         编辑项目人员({{ projectUsers.length }})
-      </button>
+      </a-button>
     </Navbar>
 
-    <h4>
+    <h4 class="operation-title">
       未完成的任务({{ pagination.total }})
-      <div class="pull-right">
-        <button
-          v-if="timeShow"
-          class="btn btn-link"
-          style="padding: 0px"
+      <div>
+        <a-button
+          v-if="timeShow" type="link" style="padding: 0px; color: #337ab7"
           @click="projectManagement(projectId)"
         >
           项目任务
-        </button>&nbsp;
-        <button
-          class="btn btn-link"
-          style="padding: 0px"
-          @click="projectArchive(projectId)"
-        >
+        </a-button>&nbsp;
+        <a-button type="link" style="padding: 0px; color: #337ab7" @click="projectArchive(projectId)">
           项目归档
-        </button>&nbsp;
-        <button
-          class="btn btn-link"
-          style="padding: 0px"
-          @click="addTrace(projectId)"
-        >
+        </a-button>&nbsp;
+        <a-button type="link" style="padding: 0px; color: #337ab7" @click="addTrace(projectId)">
           添加任务
-        </button>
+        </a-button>
       </div>
     </h4>
-    <div class="m-container">
-      <div class="input-group">
-        <input
-          v-model="content"
-          autocomplete="off"
-          type="text"
-          class="form-control"
-          placeholder="任务名称、任务内容"
-          @blur="listNotFinishedTask"
-          @keyup.enter="listNotFinishedTask"
-        />
-        <span class="input-group-btn">
-          <button
-            style="width: 100%; background-color: #007aff; color: white"
-            type="button"
-            class="btn btn-blue search-button"
-            @click="listNotFinishedTask()"
-          >
-            查询
-          </button>
-        </span>
-      </div>
+    <div>
+      <QueryWidget ref="queryWidget" placeholder-text="任务名称、任务内容" @search="query()" @value-changed="query()" />
     </div>
-    <div
-      class="panel panel-default"
-      style="margin-bottom: 10px; margin-top: 10px"
-    >
-      <div class="panel-body">
-        <vuedraggable
-          v-model="noFinishedTraces"
-          item-key="id"
-          class="wrapper"
-          :disabled="true"
-          :delay="500"
-          :delay-on-touch-only="true"
-          @change="end"
-        >
-          <template #item="{ element }">
-            <div style="margin-top: 5px; cursor: pointer">
+
+    <div v-if="noFinishedTraces && noFinishedTraces.length" class="form-group list-box">
+      <vuedraggable v-model="noFinishedTraces" :disabled="true" item-key="id" class="wrapper" @change="end">
+        <template #item="{ element }">
+          <div style="margin-top: 5px; cursor: pointer">
+            <span>
               <span>
-                <span>
-                  <Checkbox
-                    :id="'trace-finish-' + element.id"
-                    v-model="element.finished"
-                    class-name="terms"
-                    class="trace-checkbox"
-                    @input="updateTracefinished(element)"
-                  />
-                </span>
-                <span @click="openLine(element)">
-                  <!-- eslint-disable-next-line -->
-                  <span class="trace-summary" :class="{ 'font-color': element.timeLineCompletion == true, }" v-html="element.summary" />
-                  <span
-                    class="glyphicon glyphicon-option-vertical trace-icon"
-                    aria-hidden="true"
-                  />
-                  <span
-                    class="label trace-user"
-                    :class="{
-                      'label-danger': element.overdue == true,
-                      'label-primary': element.overdue != true,
-                    }"
-                  >
-                    <!-- eslint-disable-next-line -->
-                    <span v-html="element.receiveUserName" />&nbsp;
-                    <span>{{ formatDate(element.planFinishedDate) }}</span>
-                  </span>
+                <a-checkbox v-model:checked="element.finished" @change="updateTracefinished(element)" />
+              </span>
+              <span @click="openLine(element)">
+                <!-- eslint-disable-next-line -->
+                <span class="trace-summary" :class="{
+                  'font-color': element.timeLineCompletion == true,
+                }"
+                >
+                  {{ element.summary }}
                 </span>
+                <a-tag class="label" :color="element.overdue ? '#d9534f' : '#337ab7'">
+                  <span>{{ element.receiveUserName }}</span>&nbsp;
+                  <span>{{ formatDate(element.planFinishedDate) }}</span>
+                </a-tag>
               </span>
-              <a
-                class="fa-pull-right"
-                style="color: blue"
-                @click="edit(element)"
-              >编辑</a>
-            </div>
-          </template>
-        </vuedraggable>
-      </div>
+              <a style="color: blue; font-size: 14px" @click="edit(element)">编辑</a>
+            </span>
+          </div>
+        </template>
+      </vuedraggable>
     </div>
-    <div
-      v-if="noFinishedTraces.length"
-      class="row"
-      style="margin-left: 0px; margin-right: 0px"
-    >
-      <div class="pull-left">
-        <span>第{{ (pagination.current_page - 1) * pagination.per_page + 1 }}-{{
-          pagination.current_page * pagination.per_page
-        }}条,共计{{ pagination.total }}条 ,每页显示</span>
-        <PageSizeSelect :size="pagination.per_page" @page-size-changed="gridSizeSelect" />
-        <span>条</span>
-      </div>
+  </div>
+  <div v-show="noFinishedTraces && noFinishedTraces.length" style="margin: 16px 0">
+    <AntdPagination ref="paginationRef" :pagination="pagination" @get-page-params="getPageParams" />
+    <a-divider />
+  </div>
 
-      <div class="pull-right">
-        <VueBootstrapPagination
-          :pagination="pagination"
-          :callback="listNotFinishedTask"
-          @click="updateRouter"
-        />
-      </div>
-    </div>
-    <hr />
 
-    <button
-      type="button"
-      class="btn btn-link"
-      style="color: green"
-      @click="openFinishedProjectTraces"
-    >
-      查看已完成的任务
-    </button>
-  </div>
+  <a-button type="link" style="color: green; padding: 0px" @click="openFinishedProjectTraces">
+    查看已完成的任务
+  </a-button>
 </template>
 
 <script>
@@ -166,10 +74,12 @@ import Common from '../common/Common.js';
 import TraceCommon from './TraceCommon.js';
 import TraceResource from './TraceResource.js';
 import vuedraggable from 'vuedraggable';
+import QueryWidget from '../widget/QueryWidget.vue';
 
 export default {
   components: {
     vuedraggable,
+    QueryWidget,
   },
   data: function () {
     return {
@@ -195,8 +105,11 @@ export default {
   watch: {
     // 监听路由是否变化
     $route(to, from) {
-      if (to.query.currentPage != from.query.currentPage) {
+      console.log(to.query.currentPage, '888888888888');
+      if (to.query.currentPage != from.query.currentPage ||
+        to.query.pageSize != from.query.pageSize) {
         this.pagination.current_page = Number(to.query.currentPage);
+        this.pagination.per_page = Number(to.query.pageSize);
         this.listNotFinishedTask();
       }
     },
@@ -213,12 +126,6 @@ export default {
     this.personnelJurisdictionSet();
   },
   methods: {
-    gridSizeSelect: function (newPageSize) {
-      this.pagination.per_page = newPageSize;
-      this.pagination.current_page = 1;
-      // 刷新界面
-      this.updateRouter();
-    },
     end(evt) {
       var _self = this;
       $.ajax({
@@ -233,7 +140,7 @@ export default {
           newIndex: evt.moved.newIndex,
           currentPage: _self.pagination.current_page,
           pageSize: _self.pagination.per_page,
-          content: _self.content,
+          content: _self.$refs.queryWidget.getSearchText(),
         },
         beforeSend: function (request) {
           Common.addTokenToRequest(request);
@@ -254,6 +161,18 @@ export default {
       this.$router.push('/trace/traceUpdate/' + trace.id);
     },
 
+    query: function () {
+      this.pagination.current_page = 1;
+      this.updateRouter();
+    },
+
+    // 页数改变
+    getPageParams: function (page, pageSize) {
+      this.pagination.current_page = page;
+      this.pagination.per_page = pageSize;
+      this.updateRouter();
+    },
+
     updateRouter: function () {
       this.$router.push({
         path: '/trace/notFinishedProjectTraces/' + this.projectId,
@@ -263,7 +182,9 @@ export default {
           pageSize: this.pagination.per_page,
         },
       });
-      //this.listNotFinishedTask();
+      if (this.pagination.current_page === 1) {
+        this.listNotFinishedTask();
+      }
     },
 
     /**
@@ -293,7 +214,7 @@ export default {
           projectId: _self.projectId,
           currentPage: _self.pagination.current_page,
           pageSize: _self.pagination.per_page,
-          content: _self.content,
+          content: _self.$refs.queryWidget.getSearchText(),
         },
         beforeSend: function (request) {
           Common.addTokenToRequest(request);
@@ -521,6 +442,9 @@ export default {
   font-size: large;
   margin-left: 5px;
   margin-right: 5px;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  line-height: 1.42857143;
+  color: #333;
 }
 
 .trace-icon {
@@ -535,4 +459,42 @@ export default {
   display: inline;
   font-size: large;
 }
-</style>
+
+.label {
+  display: inline;
+  padding: 0.2em 0.6em 0.3em;
+  font-size: 75%;
+  font-weight: 700;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  border-radius: 0.25em;
+}
+
+.operation-title {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin: 4px 8px !important;
+}
+
+h4 {
+  font-family: inherit;
+  font-weight: 500;
+  line-height: 1.1;
+  color: inherit;
+}
+
+.list-box {
+  margin-top: 10px;
+  border: 1px solid #ddd;
+  padding: 10px 16px;
+  border-radius: 6px;
+}
+
+:deep(.ant-divider-horizontal) {
+  margin: 6px 0 !important;
+}
+</style>

+ 127 - 103
src/trace/ProjectAdminUserList.vue

@@ -1,90 +1,73 @@
 <template>
-  <div class="container-fluid">
-    <Navbar
-      :title="projectName + '-项目人员'"
-      :is-go-back="true"
-    />
-
-    <button
-      type="button"
-      class="btn btn-success"
-      style="margin-bottom: 10px; margin-top: 10px;"
-      @click="edit"
-    >
-      编辑
-    </button>
-
-    <div v-if="editFlag">
-      <div class="row">
-        <div
+  <div class="project-admin-container">
+    <Navbar :title="projectName + '-项目人员'" :is-go-back="true" />
+
+    <a-space direction="vertical" :size="16" class="user-container">
+      <a-button type="primary" @click="edit">
+        {{ editFlag ? "取消编辑" : "编辑" }}
+      </a-button>
+
+      <a-row v-if="editFlag" :gutter="[16, 16]">
+        <a-col
           v-for="user in userList"
           :key="user.id"
-          class="col-sm-6 col-md-4"
+          :xs="24"
+          :sm="12"
+          :md="8"
+          :lg="6"
         >
-          <div class="thumbnail">
-            <div class="caption">
-              <h4>
-                <input
-                  v-model="user.selected"
-                  autocomplete="off"
-                  type="checkbox"
-                  :value="user.userId"
-                />
+          <a-card hoverable>
+            <a-checkbox v-model:checked="user.selected" class="user-checkbox">
+              <a-typography-title :level="4">
                 {{ user.name }}
-              </h4>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      <button
-        type="button"
-        class="btn btn-danger"
-        @click="save()"
-      >
-        保存项目管理人员
-      </button>
-    </div>
-
-    <div v-else>
-      <p
-        v-if="projectItemUsers == null || projectItemUsers.length == 0"
-        class="bg-danger"
-        style="padding: 15px;"
-      >
-        该项目未有任何人员参与
-      </p>
-      <div class="row">
-        <div
-          v-for="user in projectItemUsers"
-          :key="user.id"
-          class="col-sm-6 col-md-4"
-        >
-          <div
-            class="thumbnail"
-            @click="openLine2(user)"
-          >
-            <div class="caption">
-              <h4>
-                {{ user.userName }}
-              </h4>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
+              </a-typography-title>
+            </a-checkbox>
+          </a-card>
+        </a-col>
+
+        <a-col :span="24">
+          <a-button type="primary" danger @click="save">
+            保存项目管理人员
+          </a-button>
+        </a-col>
+      </a-row>
+
+      <template v-else>
+        <template v-if="projectItemUsers?.length">
+          <a-row :gutter="[16, 16]">
+            <a-col
+              v-for="user in projectItemUsers"
+              :key="user.id"
+              :xs="24"
+              :sm="12"
+              :md="8"
+              :lg="6"
+            >
+              <a-card hoverable @click="openLine2(user)">
+                <a-typography-title :level="4">
+                  {{ user.userName }}
+                </a-typography-title>
+              </a-card>
+            </a-col>
+          </a-row>
+        </template>
+        <a-alert
+          v-else
+          type="warning"
+          message="该项目未有任何人员参与"
+          show-icon
+        />
+      </template>
+    </a-space>
   </div>
 </template>
 
 <script>
-
 import Common from '../common/Common.js';
 import { Uuid, Notify } from 'pc-component-v3';
 
 export default {
-  components: {
-    
-  },
+  components: {},
   data: function () {
     return {
       projectId: '',
@@ -102,17 +85,17 @@ export default {
   },
   methods: {
     /**
-         * 初始化项目
-         */
+     * 初始化项目
+     */
     initData: function () {
       this.projectId = this.$route.params.projectId;
       this.projectName = this.$route.query.projectName;
     },
-        
+
     /**
-         * 加载所有的人员
-         * @author ZhangTeng 20190212
-         */
+     * 加载所有的人员
+     * @author ZhangTeng 20190212
+     */
     initUsers: function () {
       var _self = this;
 
@@ -142,31 +125,37 @@ export default {
     },
 
     /**
-         * 打开用户的任务
-         */
+     * 打开用户的任务
+     */
     openLine2: function (user) {
-      var uuid = Uuid.createUUID();
-      var obj={
-        userId:user.userId,
-        userName:user.userName,
-      };
-      localStorage.setItem(uuid, JSON.stringify(obj));
-      this.$router.push('/trace/userNotFinishedTrace/' + uuid);
+      // var uuid = Uuid.createUUID();
+      // var obj = {
+      //   userId: user.userId,
+      //   userName: user.userName,
+      // };
+      // localStorage.setItem(uuid, JSON.stringify(obj));
+      // this.$router.push('/trace/userNotFinishedTrace/' + uuid);
+      this.$router.push({
+        path: '/trace/userNotFinishedTrace/' + user.userId,
+        query: {
+          userName: user.name,
+        },
+      });
     },
 
     /**
-         * 编辑人员
-         * @author ZhangTeng 20190212
-         */
+     * 编辑人员
+     * @author ZhangTeng 20190212
+     */
     edit: function () {
       this.editFlag = !this.editFlag;
       this.initProjectItemUser();
     },
 
     /**
-         * 初始化项目用户
-         * @author ZhangTeng 20190212
-         */
+     * 初始化项目用户
+     * @author ZhangTeng 20190212
+     */
     initProjectItemUser: function () {
       var _self = this;
 
@@ -174,18 +163,20 @@ export default {
       _self.projectItemUsers.splice(0, _self.projectItemUsers.length);
 
       $.ajax({
-        url: Common.getApiURL('ProjectItemAdminUserResource/listByProjectItemId'),
+        url: Common.getApiURL(
+          'ProjectItemAdminUserResource/listByProjectItemId',
+        ),
         type: 'get',
         dataType: 'json',
         contentType: 'application/json',
         data: {
-          'projectItemId': this.projectId,
+          projectItemId: this.projectId,
         },
         beforeSend: function (request) {
           Common.addTokenToRequest(request);
         },
         success: function (data) {
-          if(data == null){
+          if (data == null) {
             return;
           }
           for (var index1 = 0; index1 < _self.userList.length; index1++) {
@@ -207,8 +198,8 @@ export default {
     },
 
     /**
-         * 保存项目用户
-         */
+     * 保存项目用户
+     */
     save: function () {
       var _self = this;
 
@@ -225,7 +216,9 @@ export default {
       }
 
       $.ajax({
-        url: Common.getApiURL('ProjectItemAdminUserResource/save?projectItemId=' + this.projectId),
+        url: Common.getApiURL(
+          'ProjectItemAdminUserResource/save?projectItemId=' + this.projectId,
+        ),
         type: 'post',
         contentType: 'application/json',
         data: JSON.stringify(userIds),
@@ -239,11 +232,42 @@ export default {
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
-
     },
   },
 };
 </script>
 
 <style scoped>
-</style>
+.user-container {
+  width: 100%;
+  margin-top: 10px;
+}
+
+.user-checkbox {
+  width: 100%;
+}
+
+.user-checkbox :deep(.ant-checkbox + span) {
+  width: 100%;
+}
+
+.ant-card {
+  cursor: pointer;
+  transition: all 0.3s;
+}
+h4 {
+  margin: 10px !important;
+  font-family: inherit;
+  font-weight: 500;
+  line-height: 1.1;
+  color: inherit;
+}
+
+:deep(.ant-card-body) {
+  padding: 12px !important;
+}
+
+.ant-card:hover {
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+</style>

+ 246 - 493
src/trace/ProjectArchive.vue

@@ -1,409 +1,247 @@
 <template>
-  <div class="">
-    <div class="">
-      <div class="flex-container">
-        <div class="flex-content">
-          <!--文件夹管理-->
-          <div class="flex-aside-left">
-            <div>
-              <Navbar :title="projectName" :is-go-back="true" />
-              <div class="flex-box">
-                <button
-                  class="btn btn-default flex-item"
-                  @click="updateProjectArchiveFolderByTaskId()"
-                >
-                  项目任务自动生成
-                </button>
-              </div>
-              <div class="flex-box">
-                <button class="btn btn-default flex-item" @click="openFolder()">
-                  新建
-                </button>
-                <button
-                  class="btn btn-default flex-item"
-                  style="margin-left: 5px"
-                  @click="openFolder4()"
-                >
-                  修改
-                </button>
-                <button
-                  class="btn btn-default flex-item"
-                  style="margin-left: 5px"
-                  @click="openDeleteFolder()"
-                >
-                  删除
-                </button>
-              </div>
-              <div id="tree" style="margin-top: 10px" />
-            </div>
-          </div>
+  <div>
+    <a-layout>
+      <a-layout-content>
+        <a-row>
+          <a-col :span="24">
+            <!-- 左侧文件夹管理 -->
+            <a-layout>
+              <a-layout-sider width="230" style="background-color: white; padding: 10px; height: calc(100vh - 80px); overflow-y: auto; border: 1px solid #e4e4e4">
+                <Navbar :title="projectName" :is-go-back="true" />
+                <a-space direction="vertical" style="width: 100%">
+                  <a-button block @click="updateProjectArchiveFolderByTaskId()">
+                    项目任务自动生成
+                  </a-button>
+                  <a-space>
+                    <a-button @click="openFolder()">新建</a-button>
+                    <a-button @click="openFolder4()">修改</a-button>
+                    <a-button @click="openDeleteFolder()">删除</a-button>
+                  </a-space>
+                  <a-tree
+                    id="tree"
+                    :show-line="true"
+                    :tree-data="projectArchiveFolderDtos"
+                    :field-names="{
+                      key: 'id',
+                      title: 'text',
+                      children: 'nodes',
+                    }"
+                    @select="onTreeSelect"
+                  />
+                </a-space>
+              </a-layout-sider>
 
-          <div class="flex-main">
-            <div>
-              <div class="flex-main-serch">
-                <div class="form-inline">
-                  <div class="flex-box">
-                    <input
+              <!-- 中间内容区 -->
+              <a-layout-content style="border-top: 1px solid #e4e4e4; border-bottom: 1px solid #e4e4e4; overflow-y: auto; height: calc(100vh - 80px)">
+                <a-card :bordered="false" style="margin-bottom: 10px">
+                  <a-space>
+                    <a-input
                       id="archiveName"
-                      v-model="param"
-                      autocomplete="off"
-                      class="form-control flex-item"
+                      v-model:value="param"
                       placeholder="归档项名称或者文件名称"
-                      @keyup.enter="queryByProjectArchiveDtosByParam()"
+                      @press-enter="queryByProjectArchiveDtosByParam()"
                     />
+                    <a-button @click="queryByProjectArchiveDtosByParam()">查询</a-button>
+                    <a-button @click="openFolder1()">新建归档项</a-button>
+                    <a-button danger @click="openDeleteProject()">删除归档项</a-button>
+                  </a-space>
+                </a-card>
 
-                    <button
-                      class="btn btn-default flex-item-1"
-                      style="margin-left: 5px"
-                      @click="queryByProjectArchiveDtosByParam()"
-                    >
-                      查询
-                    </button>
-                    <button
-                      class="btn btn-default flex-item-1"
-                      style="margin-left: 5px"
-                      @click="openFolder1()"
-                    >
-                      新建归档项
-                    </button>
-                    <button
-                      class="btn btn-danger flex-item-1"
-                      style="margin-left: 5px"
-                      @click="openDeleteProject()"
-                    >
-                      删除归档项
-                    </button>
-                  </div>
-                </div>
-              </div>
+                <a-table
+                  :data-source="projectArchiveDtos"
+                  :row-class-name="(record) => projectArchivesId === record.id ? 'ant-table-row-selected' : ''"
+                  :pagination="false"
+                  row-key="id"
+                  @row-click="trClick"
+                >
+                  <a-table-column key="name" title="归档项名称" data-index="name" />
+                  <a-table-column key="createDate" title="创建时间" data-index="createDate" />
+                  <a-table-column key="planArchiveDate" title="计划归档日期" data-index="planArchiveDate" />
+                  <a-table-column key="fileName" title="归档文件名" data-index="fileName" />
+                  <a-table-column key="archiveUserName" title="归档人" data-index="archiveUserName" />
+                  <a-table-column key="archiveDate" title="归档时间" data-index="archiveDate" />
+                </a-table>
+              </a-layout-content>
 
-              <table class="table" style="margin-top: 10px">
-                <thead>
-                  <tr>
-                    <!--<th>
-									项目名称
-								</th>-->
-                    <th>归档项名称</th>
-                    <th>创建时间</th>
-                    <th>计划归档日期</th>
-                    <th>归档文件名</th>
-                    <!--<th>
-									归档文件大小
-								</th>-->
-                    <th>归档人</th>
-                    <th>归档时间</th>
-                  </tr>
-                </thead>
-                <tbody>
-                  <tr
-                    v-for="item in projectArchiveDtos"
-                    :key="item"
-                    :class="projectArchivesId == item.id ? 'success' : ''"
-                    @click="trClick(item)"
-                    @dblclick="openFolder2()"
-                  >
-                    <!--<td>
-									{{item.projectItemName}}
-								</td>-->
-                    <td>
-                      {{ item.name }}
-                    </td>
-                    <td>
-                      {{ item.createDate }}
-                    </td>
-                    <td>
-                      {{ item.planArchiveDate }}
-                    </td>
-                    <td>
-                      {{ item.fileName }}
-                    </td>
-                    <!--<td>
-									{{item.fileSize}}
-								</td>-->
-                    <td>
-                      {{ item.archiveUserName }}
-                    </td>
-                    <td>
-                      {{ item.archiveDate }}
-                    </td>
-                  </tr>
-                </tbody>
-              </table>
-            </div>
-          </div>
-          <!--归档项下载和历史记录-->
-          <div class="flex-aside-right">
-            <div>
-              <div id="tabs-911500" class="tabbable">
-                <ul class="nav nav-tabs">
-                  <li class="active">
-                    <a href="#panel1" data-toggle="tab">详细信息</a>
-                  </li>
-                  <li>
-                    <a href="#panel3" data-toggle="tab">历史记录</a>
-                  </li>
-                </ul>
-                <div class="tab-content">
-                  <div id="panel1" class="tab-pane active">
-                    <div
-                      v-if="
-                        projectArchivesDto.fileName != '' &&
-                          projectArchivesDto.fileName != undefined
-                      "
-                      class="row m-row"
-                    >
-                      <div class="col-sm-12">
-                        <div class="thumbnail">
-                          <div class="caption">
-                            <p>
-                              <strong>文件名:</strong>{{ projectArchivesDto.fileName }}
-                            </p>
-                            <p>
-                              <strong>文件大小:</strong>{{ projectArchivesDto.fileSize }}KB
-                            </p>
-                            <p>
-                              <strong>上传人:</strong>{{ projectArchivesDto.archiveUserName }}
-                            </p>
-                            <p>
-                              <strong>上传时间:</strong>{{ projectArchivesDto.archiveDate }}
-                            </p>
-                            <button
-                              class="btn btn-link"
-                              @click="download(projectArchivesDto.fileName)"
-                            >
-                              下载
-                            </button>
-                          </div>
-                        </div>
-                      </div>
+              <!-- 右侧详情区域 -->
+              <a-layout-sider width="300" style="background-color: white; padding: 10px; height: calc(100vh - 80px); overflow-y: auto; border: 1px solid #e4e4e4">
+                <a-tabs default-active-key="1">
+                  <a-tab-pane key="1" tab="详细信息">
+                    <div v-if="projectArchivesDto.fileName && projectArchivesDto.fileName !== ''">
+                      <a-card>
+                        <a-typography>
+                          <a-typography-paragraph>
+                            <a-typography-text strong>文件名:</a-typography-text>{{ projectArchivesDto.fileName }}
+                          </a-typography-paragraph>
+                          <a-typography-paragraph>
+                            <a-typography-text strong>文件大小:</a-typography-text>{{ projectArchivesDto.fileSize }}KB
+                          </a-typography-paragraph>
+                          <a-typography-paragraph>
+                            <a-typography-text strong>上传人:</a-typography-text>{{ projectArchivesDto.archiveUserName }}
+                          </a-typography-paragraph>
+                          <a-typography-paragraph>
+                            <a-typography-text strong>上传时间:</a-typography-text>{{ projectArchivesDto.archiveDate }}
+                          </a-typography-paragraph>
+                        </a-typography>
+                        <a-button type="link" @click="download(projectArchivesDto.fileName)">下载</a-button>
+                      </a-card>
                     </div>
-                  </div>
-                  <div id="panel3" class="tab-pane">
-                    <template v-for="item in projectArchiveAuditDtos">
-                      <div
-                        v-if="item.fileName != '' && item.fileName != undefined"
-                        :key="item.id"
-                        class="row m-row"
-                      >
-                        <div class="col-sm-12">
-                          <div class="thumbnail">
-                            <div class="caption">
-                              <p>
-                                <strong>文件名:</strong>{{ item.fileName }}
-                              </p>
-                              <p>
-                                <strong>文件大小:</strong>{{ item.fileSize }}KB
-                              </p>
-                              <p>
-                                <strong>上传人:</strong>{{ item.archiveUserName }}
-                              </p>
-                              <p>
-                                <strong>上传时间:</strong>{{ item.archiveDate }}
-                              </p>
-                              <button
-                                class="btn btn-link"
-                                @click="download(item.fileName)"
-                              >
-                                下载
-                              </button>
-                            </div>
-                          </div>
-                        </div>
-                      </div>
-                    </template>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
+                  </a-tab-pane>
+                  <a-tab-pane key="2" tab="历史记录">
+                    <a-list item-layout="vertical">
+                      <a-list-item v-for="item in projectArchiveAuditDtos" :key="item.id">
+                        <template v-if="item.fileName && item.fileName !== ''">
+                          <a-card>
+                            <a-typography>
+                              <a-typography-paragraph>
+                                <a-typography-text strong>文件名:</a-typography-text>{{ item.fileName }}
+                              </a-typography-paragraph>
+                              <a-typography-paragraph>
+                                <a-typography-text strong>文件大小:</a-typography-text>{{ item.fileSize }}KB
+                              </a-typography-paragraph>
+                              <a-typography-paragraph>
+                                <a-typography-text strong>上传人:</a-typography-text>{{ item.archiveUserName }}
+                              </a-typography-paragraph>
+                              <a-typography-paragraph>
+                                <a-typography-text strong>上传时间:</a-typography-text>{{ item.archiveDate }}
+                              </a-typography-paragraph>
+                            </a-typography>
+                            <a-button type="link" @click="download(item.fileName)">下载</a-button>
+                          </a-card>
+                        </template>
+                      </a-list-item>
+                    </a-list>
+                  </a-tab-pane>
+                </a-tabs>
+              </a-layout-sider>
+            </a-layout>
+          </a-col>
+        </a-row>
+      </a-layout-content>
+    </a-layout>
 
-    <div>
-      <Modal
-        v-model:show="modal1"
-        :small="true"
-        :show-ok-button="true"
-        :show-canel-button="true"
-        @ok="saveProjectArchiveFolder"
-        @cancel="cancel"
-      >
-        <template #header>
-          {{ type + "文件夹" }}
-        </template>
-        <label>文件名</label>
-        <input
-          v-model="folderName"
-          autocomplete="off"
-          type="text"
-          class="form-control"
-          placeholder="文件夹名称"
-        />
-      </Modal>
+    <!-- 新建文件夹弹窗 -->
+    <a-modal
+      v-model:open="modal1"
+      :title="type + '文件夹'"
+      @ok="saveProjectArchiveFolder"
+      @cancel="cancel"
+    >
+      <a-form layout="vertical">
+        <a-form-item label="文件名">
+          <a-input v-model:value="folderName" placeholder="文件夹名称" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
 
-      <Modal
-        v-model:show="modal4"
-        :small="true"
-        :show-ok-button="true"
-        :show-canel-button="true"
-        @ok="updateProjectArchiveFolder"
-        @cancel="cancel2"
-      >
-        <template #header>
-          {{ type + "文件夹" }}
-        </template>
-        <div class="form-group">
-          <label for="projectFolder">上级文件名</label>
+    <!-- 修改文件夹弹窗 -->
+    <a-modal
+      v-model:open="modal4"
+      :title="type + '文件夹'"
+      @ok="updateProjectArchiveFolder"
+      @cancel="cancel2"
+    >
+      <a-form layout="vertical">
+        <a-form-item label="上级文件名">
           <a-select
             v-model:value="project"
             allow-clear
-            style="width: 100%; height: 30px"
+            style="width: 100%"
             :options="allProject"
             @change="projectChange"
           />
-          <!-- <select id="projectFolder" class="form-control" /> -->
-        </div>
-        <div class="form-group">
-          <label>文件名</label>
-          <input
-            v-model="folderName"
-            autocomplete="off"
-            type="text"
-            class="form-control"
-            placeholder="文件夹名称"
-          />
-        </div>
-      </Modal>
+        </a-form-item>
+        <a-form-item label="文件名">
+          <a-input v-model:value="folderName" placeholder="文件夹名称" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
 
-      <!-- 是否删除文件夹 -->
-      <Modal
-        v-model:show="modal5"
-        :small="true"
-        :show-ok-button="true"
-        :show-canel-button="true"
-        @ok="deleteFolder"
-        @cancel="cancelFolder"
-      >
-        <template #header>
-          {{ "删除文件夹" }}
-        </template>
-        <h3>
-          您确认要删除{{
-            folderName
-          }}文件夹吗?如果是的话,请点击【确定】按钮,否则点击【取消】按钮。
-        </h3>
-      </Modal>
-      <!-- 是否删除归档项 -->
-      <Modal
-        v-model:show="modal6"
-        :small="true"
-        :show-ok-button="true"
-        :show-canel-button="true"
-        @ok="deleteProjectArchives"
-        @cancel="cancelProject"
-      >
-        <template #header>
-          {{ "删除归档项" }}
-        </template>
-        <h3>
-          您确认要删除{{
-            projectArchivesDto.name
-          }}归档项吗?如果是的话,请点击【确定】按钮,否则点击【取消】按钮。
-        </h3>
-      </Modal>
+    <!-- 删除文件夹确认弹窗 -->
+    <a-modal
+      v-model:open="modal5"
+      title="删除文件夹"
+      @ok="deleteFolder"
+      @cancel="cancelFolder"
+    >
+      <a-typography>
+        <a-typography-title :level="5">您确认要删除 {{ folderName }} 文件夹吗?</a-typography-title>
+        <a-typography-paragraph>
+          如果是的话,请点击【确定】按钮,否则点击【取消】按钮。
+        </a-typography-paragraph>
+      </a-typography>
+    </a-modal>
 
-      <!--
-        作者:GuoZhiBo
-        时间:2019-10-14
-        描述:归档项新增和修改
-      -->
-      <Modal
-        v-model:show="modal2"
-        :small="true"
-        :show-ok-button="true"
-        :show-canel-button="true"
-        @ok="saveProjectArchives"
-        @cancel="cancel1"
-      >
-        <template #header>
-          {{ type + "归档项" }}
-        </template>
-        <div class="form-group">
-          <label for="exampleInputEmail2">归档项名称</label>
-          <input
-            v-model="projectArchivesDto.name"
-            autocomplete="off"
-            style="width: 100%"
-            class="form-control"
-          />
-        </div>
-        <div class="form-group">
-          <label for="exampleInputEmail2">计划归档日期</label>
+    <!-- 删除归档项确认弹窗 -->
+    <a-modal
+      v-model:open="modal6"
+      title="删除归档项"
+      @ok="deleteProjectArchives"
+      @cancel="cancelProject"
+    >
+      <a-typography>
+        <a-typography-title :level="5">您确认要删除 {{ projectArchivesDto.name }} 归档项吗?</a-typography-title>
+        <a-typography-paragraph>
+          如果是的话,请点击【确定】按钮,否则点击【取消】按钮。
+        </a-typography-paragraph>
+      </a-typography>
+    </a-modal>
+
+    <!-- 归档项新增和修改弹窗 -->
+    <a-modal
+      v-model:open="modal2"
+      :title="type + '归档项'"
+      @ok="saveProjectArchives"
+      @cancel="cancel1"
+    >
+      <a-form layout="vertical">
+        <a-form-item label="归档项名称">
+          <a-input v-model:value="projectArchivesDto.name" style="width: 100%" />
+        </a-form-item>
+        <a-form-item label="计划归档日期">
           <Date v-model="planArchiveDate" style="width: 100%" />
-        </div>
-        <div class="form-group">
-          <label for="exampleInputEmail2">文件名称</label>
-          <input
-            v-model="projectArchivesDto.fileName"
-            autocomplete="off"
-            style="width: 100%"
-            class="form-control"
-            readonly="readonly"
-          />
-          <!--<span @click="projectArchivesDto.fileName = ''">删除</span>-->
-        </div>
-        <div class="form-group">
-          <label for="exampleInputEmail2">文件大小(KB)</label>
-          <input
-            v-model="projectArchivesDto.fileSize"
-            autocomplete="off"
-            style="width: 100%"
-            class="form-control"
-            readonly="readonly"
-          />
-        </div>
-        <div>
-          <input
-            ref="fileInput"
-            autocomplete="off"
-            type="file"
-            class="form-control file-input"
-            @change="onFileChanges"
-          />
-          <label for="attachment">
-            <a
-              role="button"
-              class="btn btn-primary btn-sm"
-              @click="clickButton"
-            >
-              <i class="fa fa-upload" />
-              &nbsp; 上传附件
-            </a>
-          </label>
-          <button
-            class="btn btn-primary"
-            @click="cleanFile(projectArchivesDto)"
+        </a-form-item>
+        <a-form-item label="文件名称">
+          <a-input v-model:value="projectArchivesDto.fileName" style="width: 100%" readonly />
+        </a-form-item>
+        <a-form-item label="文件大小(KB)">
+          <a-input v-model:value="projectArchivesDto.fileSize" style="width: 100%" readonly />
+        </a-form-item>
+        <a-space>
+          <a-upload
+            :file-list="[]"
+            :show-upload-list="false"
+            :before-upload="(file) => {
+              uploadFile(file);
+              return false;
+            }"
           >
-            清空文件
-          </button>
-        </div>
-      </Modal>
-    </div>
-    <div>
-      <Loading v-if="loading" />
-    </div>
+            <a-button type="primary">
+              <upload-outlined />
+              上传附件
+            </a-button>
+          </a-upload>
+          <a-button @click="cleanFile(projectArchivesDto)">清空文件</a-button>
+        </a-space>
+      </a-form>
+    </a-modal>
+
+    <!-- <a-spin :spinning="loading" tip="加载中..." size="large" /> -->
+    <Loading v-if="loading" />
   </div>
 </template>
-<script type="text/javascript">
+
+<script>
 import Common from '../common/Common.js';
 import { Notify } from 'pc-component-v3';
 import UpladFile from '../widget/UpladFile.js';
 import { message } from 'ant-design-vue';
+import { UploadOutlined } from '@ant-design/icons-vue';
 
 export default {
   components: {
     UpladFile,
+    UploadOutlined,
   },
   data: function () {
     return {
@@ -411,7 +249,6 @@ export default {
       projectName: undefined,
       projectArchiveFolderDtos: [], //文件夹dtos
       folderName: undefined, //文件夹名称
-      tree: undefined,
       planArchiveDate: '',
       projectArchiveDtos: [], //归档项Dto
       projectArchivesDto: {
@@ -469,8 +306,21 @@ export default {
     }, 300);
   },
 
-  beforeUnmount: function () {},
   methods: {
+    // 树节点选择事件
+    onTreeSelect(selectedKeys, e) {
+      console.log(selectedKeys, e, 'selectedKeys, e');
+      if (selectedKeys.length > 0) {
+        this.folderId = selectedKeys[0];
+        this.folderName = e.node.text;
+        this.parentProjectArchiveFolderId = e.node.parentProjectArchiveFolderId;
+      } else {
+        this.folderId = undefined;
+        this.folderName = undefined;
+        this.parentProjectArchiveFolderId = undefined;
+      }
+    },
+
     /**
      * 清空文件
      * @param {Object} item
@@ -518,19 +368,12 @@ export default {
     },
     /**
      * 表格点击事件
-     * @param {Object} item
+     * @param {Object} record
      */
-    trClick: function (item) {
+    trClick: function (record) {
       var _self = this;
-      _self.projectArchivesId = item.id;
-      _self.projectArchivesDto = item;
-    },
-    /**
-     * 点击上传按钮事件
-     * @return {[type]} [description]
-     */
-    clickButton: function () {
-      $(this.$refs.fileInput).click();
+      _self.projectArchivesId = record.id;
+      _self.projectArchivesDto = record;
     },
 
     /**
@@ -612,6 +455,7 @@ export default {
      */
     initData: function () {
       var _self = this;
+      _self.loading = true;
       $.ajax({
         url: Common.getApiURL(
           'ProjectArchiveFolderResource/queryByProjectArchiveFolderDtos',
@@ -629,21 +473,10 @@ export default {
           _self.projectArchiveFolderDtos = data;
           _self.folderId = undefined;
           _self.folderName = undefined;
-          $('#tree').treeview({
-            data: _self.projectArchiveFolderDtos,
-            onNodeSelected: function (event, data) {
-              _self.folderId = data.id;
-              _self.folderName = data.name;
-              _self.parentProjectArchiveFolderId =
-                data.parentProjectArchiveFolderId;
-            },
-            onNodeUnselected: function (event, data) {
-              _self.folderId = undefined;
-              _self.parentProjectArchiveFolderId = undefined;
-            },
-          });
+          _self.loading = false;
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
+          _self.loading = false;
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
@@ -714,6 +547,7 @@ export default {
     //查询当前文件夹下面所有的归档项
     queryByProjectArchiveDtos: function () {
       var _self = this;
+      _self.loading = true;
       $.ajax({
         url: Common.getApiURL(
           'ProjectArchivesResource/queryByProjectArchiveDtos',
@@ -729,8 +563,10 @@ export default {
         },
         success: function (data) {
           _self.projectArchiveDtos = data;
+          _self.loading = false;
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
+          _self.loading = false;
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
@@ -804,31 +640,18 @@ export default {
                 _self.projectArchivesDto.fileName = fileName;
                 _self.projectArchivesDto.fileSize =
                   Math.round((selectedFile.size / 1024) * 100) / 100;
-                // _self.$set(_self.projectArchivesDto, 'fileName', fileName);
-                // _self.$set(
-                //   _self.projectArchivesDto,
-                //   'fileSize',
-                //   Math.round((selectedFile.size / 1024) * 100) / 100,
-                // );
                 if (
                   _self.projectArchivesDto.name == null ||
                   _self.projectArchivesDto.name == ''
                 ) {
                   _self.projectArchivesDto.name = fileName;
-                  // _self.$set(_self.projectArchivesDto, 'name', fileName);
                 }
                 if (
                   _self.projectArchivesDto.planArchiveDate == null ||
                   _self.projectArchivesDto.planArchiveDate == ''
                 ) {
-                  // _self.projectArchivesDto.planArchiveDate = dayjs().format('YYYY-MM-DD HH:mm:ss');
                   _self.projectArchivesDto.planArchiveDate =
                     _self.planArchiveDate + ' 00:00:00';
-                  // _self.$set(
-                  //   _self.projectArchivesDto,
-                  //   'planArchiveDate',
-                  //   dayjs().format('YYYY-MM-DD HH:mm:ss'),
-                  // );
                 }
 
                 _self.saveProjectArchives();
@@ -1008,6 +831,7 @@ export default {
      */
     queryByProjectArchiveDtoAudit: function () {
       var _self = this;
+      _self.loading = true;
       $.ajax({
         url: Common.getApiURL(
           'ProjectArchivesResource/queryByProjectArchiveDtoAudit',
@@ -1023,8 +847,10 @@ export default {
         },
         success: function (data) {
           _self.projectArchiveAuditDtos = data;
+          _self.loading = false;
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
+          _self.loading = false;
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
@@ -1108,85 +934,12 @@ export default {
 </script>
 
 <style scoped>
-.flex-container {
-  display: flex;
-  /* 垂直*/
-  flex-direction: column;
-  width: 100%;
-  /*视口被均分为100单位的vh 占据整个窗口,扣掉顶部topNav的距离后,计算得到container的高度*/
-  height: calc(100vh - 80px);
-}
-
-.flex-content {
-  display: flex;
-  flex: 1;
-  height: calc(100vh - 80px);
-}
-
-.flex-aside-left {
-  flex: 0 0 300px;
-  /* margin-right: 10px; */
-  border: 1px solid #e4e4e4;
-  background-color: white;
-  padding: 10px;
-  height: calc(100vh - 80px);
-  overflow-y: auto;
-}
-
-.flex-aside-right {
-  flex: 0 0 300px;
-  /* margin-left: 10px; */
-  border: 1px solid #e4e4e4;
-  background-color: white;
-  padding: 10px;
-  height: calc(100vh - 80px);
-  overflow-y: auto;
-}
-
-.flex-main {
-  overflow: auto;
-  flex: 1;
-  border-top: 1px solid #e4e4e4;
-  border-bottom: 1px solid #e4e4e4;
-  overflow-y: auto;
-}
-
-.flex-main-serch {
-  padding: 10px;
-  background-color: white;
-  border-bottom: 1px solid #e4e4e4;
-}
-
-tr:hover {
-  background-color: #eee;
-}
-
-.file-input {
-  width: 0.1px;
-  height: 0.1px;
-  opacity: 0;
-  overflow: hidden;
-  position: absolute;
-  z-index: -1;
-}
-
-.m-row {
-  margin-top: 10px;
-}
-
-.flex-box {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: nowrap;
-  justify-content: center;
-  margin: 5px 0;
-}
-
-.flex-item {
-  flex: 1 1 auto;
+/* 覆盖Ant Design表格选中行样式 */
+:deep(.ant-table-row-selected > td) {
+  background-color: #e6f7ff !important;
 }
 
-.flex-item-1 {
-  flex: 0 0 auto;
+:deep(.ant-table-row:hover > td) {
+  background-color: #f5f5f5;
 }
 </style>

+ 104 - 126
src/trace/ProjectList.vue

@@ -1,60 +1,59 @@
 <template>
-  <div>
+  <a-row>
     <TraceHeader :type="'traceProject'" />
-    <div>
-      <div>
-        <QueryWidget
-          ref="queryWidget"
-          @search="getDatas()"
-          @value-changed="getDatas()"
-        />
-        <p
-          v-if="projectList == null || projectList.length == 0"
-          class="bg-danger"
-          style="padding: 15px; margin-top: 15px"
+  </a-row>
+  
+  <div class="query-box">
+    <QueryWidget ref="queryWidget" @search="query" @value-changed="query" />
+  </div>
+  
+  <a-row>
+    <a-alert v-if="projectList.length === 0" style="width: 100%;" message="您未参与任何项目" type="warning" />
+
+    <!-- 项目列表,使用a-row和a-col实现均匀排列 -->
+    <a-row :gutter="[16, 16]" class="list-box">
+      <a-col
+        v-for="item in projectList"
+        :key="item.id"
+        :xs="24"
+        :sm="12"
+        :md="8"
+      >
+        <a-card
+          hoverable
+          :body-style="{ padding: '18px', cursor: 'pointer' }"
+          @click="openLine(item)"
         >
-          您未参与任何项目~
-        </p>
-        <div class="row" style="margin-top: 10px">
-          <div v-for="item in projectList" :key="item.id" class="col-md-4">
-            <div class="panel panel-default" @click="openLine(item)">
-              <div class="panel-body" style="height: 70px">
-                <div>
-                  <div>{{ item.name }}</div>
-                  <div>
-                    <span>{{ item.no }}</span>
-                    <span v-if="item.admin" style="color: red"> 参与中 </span>
-                  </div>
-                </div>
-              </div>
+          <div>
+            <div style="font-weight: 500">{{ item.name }}</div>
+            <div>
+              <span>{{ item.no }}</span>
+              <a-tag v-if="item.admin" color="red" style="margin-left: 8px">
+                参与中
+              </a-tag>
             </div>
           </div>
-        </div>
-
-        <div class="row">
-          <div class="col-md-12">
-            <VueBootstrapPagination
-              :pagination="pagination"
-              :callback="getDatas"
-              @click="updateRouter"
-            />
-          </div>
-        </div>
-
-        <hr />
-        <button
-          type="button"
-          class="btn btn-link"
-          style="color: green"
-          @click="isTheArchive"
-        >
-          已归档的项目
-        </button>
-      </div>
-
-      <h3>&nbsp;</h3>
-    </div>
+        </a-card>
+      </a-col>
+    </a-row>
+  </a-row>
+  <div v-show="projectList && projectList.length" class="pagination-container">
+    <AntdPagination
+      ref="paginationRef"
+      :pagination="pagination"
+      @get-page-params="getPageParams"
+    />
   </div>
+  
+  <a-button
+    type="link"
+    class="archive-button"
+    @click="isTheArchive"
+  >
+    已归档的项目
+  </a-button>
+  
+  <Loading v-if="loading" />
 </template>
 
 <script>
@@ -67,7 +66,6 @@ import TraceHeader from './TraceHeader.vue';
 export default {
   components: {
     QueryWidget,
-
     TraceHeader,
   },
   data: function () {
@@ -96,6 +94,7 @@ export default {
       traceState: undefined,
       traceStatus: undefined,
       traceUserStatus: undefined,
+      loading: false,
       //flag: false
     };
   },
@@ -116,30 +115,15 @@ export default {
       this.pagination.per_page = Number(this.$route.query.pageSize);
     }
     this.updateRouter();
-
-    // this.getDatas();
-    /**
-    var uuid = this.$route.params.uuid;
-    if (uuid != null && uuid != undefined) {
-      var str = localStorage.getItem(uuid);
-
-      if (str != null) {
-        var object = JSON.parse(str);
-        var userId = JSON.parse(localStorage.json_LoginInfo).userId;
-
-        if (object.traceState != undefined) {
-          this.traceState = object.traceState;
-        }
-        if (object.traceStatus != undefined) {
-          this.traceStatus = object.traceStatus;
-        }
-        if (object.traceUserStatus != undefined) {
-          this.traceUserStatus = object.traceUserStatus;
-        }
-      }
-    } */
   },
   methods: {
+    // 页数改变
+    getPageParams: function (page, pageSize) {
+      this.pagination.current_page = page;
+      this.pagination.per_page = pageSize;
+      this.updateRouter();
+    },
+
     updateRouter: function () {
       this.$router.push({
         path: '/trace/projectList',
@@ -150,6 +134,11 @@ export default {
       });
       this.getDatas();
     },
+
+    query: function () {
+      this.$refs.paginationRef.backFirstPage();
+    },
+
     /**
      * 查询数据
      * @author GuoZhiBo 20180226
@@ -161,18 +150,11 @@ export default {
         start: (_self.pagination.current_page - 1) * _self.pagination.per_page,
         length: _self.pagination.per_page,
       };
-      var val = _self.$refs.queryWidget.getSearchText();
-      if (val != undefined && val.length > 0) {
-        _self.range = {
-          start: 0,
-          length: _self.pagination.per_page,
-        };
-      }
       var queryParam = {
         range: _self.range,
         condition: _self.$refs.queryWidget.getSearchText(),
       };
-
+      _self.loading = true;
       $.ajax({
         url: Common.getApiURL(
           'ProjectItemUserResource/queryProjectItemUserDto',
@@ -190,8 +172,10 @@ export default {
           _self.pagination.last_page = Math.ceil(
             data.totalSize / data.range.length,
           );
+          _self.loading = false;
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
+          _self.loading = false;
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
@@ -227,56 +211,50 @@ export default {
 </script>
 
 <style scoped>
-.mui-card {
-  margin: 0px;
-  margin-top: 1px;
-}
-.mui-card-footer:before,
-.mui-card-header:after {
-  background-color: #ffffff;
-}
-.mui-card-content {
-  padding: 0px 10px;
-}
-.mui-card-content p {
-  margin: 0px;
-}
-p {
-  font-size: 14px !important;
-  padding: 2px 0px;
-  color: #000000;
+/* 查询框样式,使其占满一行 */
+.query-box {
+  width: 100%;
+  padding: 16px 8px;
+  margin-bottom: 8px;
+  box-sizing: border-box;
 }
-.time {
-  /*color: #8f8f94;*/
-  font-weight: bold;
-}
-.index {
-  font-size: 16px;
-  font-weight: bold;
+
+/* 项目列表容器样式 */
+.list-box {
+  width: 100%;
+  margin: 0 !important;
+  padding: 0 !important;
+  box-sizing: border-box;
 }
-.div-statis {
-  margin-bottom: 5px;
-  font-size: 0.8em;
-  text-align: center;
+
+/* 分页容器样式 */
+.pagination-container {
+  padding: 16px 8px;
+  margin-top: 8px;
 }
-.divs {
-  margin-top: 2%;
-  margin-left: 39%;
+
+/* 归档按钮样式 */
+.archive-button {
+  color: #52c41a;
+  padding-left: 8px;
+  margin-top: 8px;
 }
-.div {
-  word-wrap: break-word;
-  white-space: normal;
-  font-size: 20px;
+
+/* 卡片样式 */
+.a-card {
+  width: 100%;
+  margin-bottom: 16px;
 }
-.div1 {
-  padding-left: 10%;
-  color: blue;
+
+.a-tag {
+  vertical-align: middle;
 }
-.div2 {
-  color: cadetblue;
+
+:deep(.ant-pagination-item-active) {
+  border-color: #1890ff;
 }
-.img {
-  height: 33px;
-  width: 38px;
+
+:deep(.ant-empty) {
+  margin: 24px 0;
 }
 </style>

+ 123 - 101
src/trace/ProjectUserList.vue

@@ -1,91 +1,74 @@
 <template>
-  <div class="container-fluid">
-    <Navbar
-      :title="projectName + '-项目人员'"
-      :is-go-back="true"
-    />
-
-    <button
-      type="button"
-      class="btn btn-success"
-      style="margin-bottom: 10px; margin-top: 10px;"
-      @click="edit"
-    >
-      编辑
-    </button>
-
-    <div v-if="editFlag">
-      <div class="row">
-        <div
+  <div class="project-admin-container">
+    <Navbar :title="projectName + '-项目人员'" :is-go-back="true" />
+
+    <a-space direction="vertical" :size="16" class="user-container">
+      <a-button type="primary" @click="edit">
+        {{ editFlag ? "取消编辑" : "编辑" }}
+      </a-button>
+
+      <a-row v-if="editFlag" :gutter="[16, 16]">
+        <a-col
           v-for="user in userList"
           :key="user.id"
-          class="col-sm-6 col-md-4"
+          :xs="24"
+          :sm="12"
+          :md="8"
+          :lg="6"
         >
-          <div class="thumbnail">
-            <div class="caption">
-              <h4>
-                <input
-                  v-model="user.selected"
-                  autocomplete="off"
-                  type="checkbox"
-                  :value="user.userId"
-                />
+          <a-card hoverable>
+            <a-checkbox v-model:checked="user.selected" class="user-checkbox">
+              <a-typography-title :level="4">
                 {{ user.name }}
-              </h4>
-            </div>
-          </div>
-        </div>
-      </div>
-
-      <button
-        type="button"
-        class="btn btn-danger"
-        @click="save()"
-      >
-        保存项目成员
-      </button>
-    </div>
-
-    <div v-else>
-      <p
-        v-if="projectItemUsers == null || projectItemUsers.length == 0"
-        class="bg-danger"
-        style="padding: 15px;"
-      >
-        该项目未有任何人员参与
-      </p>
-      <div class="row">
-        <div
-          v-for="user in projectItemUsers"
-          :key="user.id"
-          class="col-sm-6 col-md-4"
-        >
-          <div
-            class="thumbnail"
-            @click="openLine2(user)"
-          >
-            <div class="caption">
-              <h4>
-                {{ user.userName }}
-              </h4>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
+              </a-typography-title>
+            </a-checkbox>
+          </a-card>
+        </a-col>
+
+        <a-col :span="24">
+          <a-button type="primary" danger @click="save">
+            保存项目管理人员
+          </a-button>
+        </a-col>
+      </a-row>
+
+      <template v-else>
+        <template v-if="projectItemUsers?.length">
+          <a-row :gutter="[16, 16]">
+            <a-col
+              v-for="user in projectItemUsers"
+              :key="user.id"
+              :xs="24"
+              :sm="12"
+              :md="8"
+              :lg="6"
+            >
+              <a-card hoverable @click="openLine2(user)">
+                <a-typography-title :level="4">
+                  {{ user.userName }}
+                </a-typography-title>
+              </a-card>
+            </a-col>
+          </a-row>
+        </template>
+        <a-alert
+          v-else
+          type="warning"
+          message="该项目未有任何人员参与"
+          show-icon
+        />
+      </template>
+    </a-space>
   </div>
 </template>
 
 <script>
-
 import Common from '../common/Common.js';
 import { Uuid } from 'pc-component-v3';
 import { Notify } from 'pc-component-v3';
 
 export default {
-  components: {
-    
-  },
+  components: {},
   data: function () {
     return {
       projectId: '',
@@ -103,17 +86,17 @@ export default {
   },
   methods: {
     /**
-         * 初始化项目
-         */
+     * 初始化项目
+     */
     initData: function () {
       this.projectId = this.$route.params.projectId;
       this.projectName = this.$route.query.projectName;
     },
-        
+
     /**
-         * 加载所有的人员
-         * @author ZhangTeng 20190212
-         */
+     * 加载所有的人员
+     * @author ZhangTeng 20190212
+     */
     initUsers: function () {
       var _self = this;
 
@@ -146,31 +129,37 @@ export default {
     },
 
     /**
-         * 打开用户的任务
-         */
+     * 打开用户的任务
+     */
     openLine2: function (user) {
       var uuid = Uuid.createUUID();
-      var obj={
-        userId:user.userId,
-        userName:user.userName,
-      };
-      localStorage.setItem(uuid, JSON.stringify(obj));
-      this.$router.push('/trace/userNotFinishedTrace/' + uuid);
+      // var obj = {
+      //   userId: user.userId,
+      //   userName: user.userName,
+      // };
+      // localStorage.setItem(uuid, JSON.stringify(obj));
+      // this.$router.push('/trace/userNotFinishedTrace/' + uuid);
+      this.$router.push({
+        path: '/trace/userNotFinishedTrace/' + user.userId,
+        query: {
+          userName: user.userName,
+        },
+      });
     },
 
     /**
-         * 编辑人员
-         * @author ZhangTeng 20190212
-         */
+     * 编辑人员
+     * @author ZhangTeng 20190212
+     */
     edit: function () {
       this.editFlag = !this.editFlag;
       this.initProjectItemUser();
     },
 
     /**
-         * 初始化项目用户
-         * @author ZhangTeng 20190212
-         */
+     * 初始化项目用户
+     * @author ZhangTeng 20190212
+     */
     initProjectItemUser: function () {
       var _self = this;
 
@@ -184,14 +173,14 @@ export default {
         dataType: 'json',
         contentType: 'application/json',
         data: {
-          'projectItemId': this.projectId,
+          projectItemId: this.projectId,
         },
         beforeSend: function (request) {
           Common.addTokenToRequest(request);
         },
         success: function (data) {
           // console.log(data);
-          if(data == null){
+          if (data == null) {
             return;
           }
           for (var index1 = 0; index1 < _self.userList.length; index1++) {
@@ -213,8 +202,8 @@ export default {
     },
 
     /**
-         * 保存项目用户
-         */
+     * 保存项目用户
+     */
     save: function () {
       var _self = this;
 
@@ -231,7 +220,9 @@ export default {
       }
 
       $.ajax({
-        url: Common.getApiURL('ProjectItemUserResource/save?projectItemId=' + this.projectId),
+        url: Common.getApiURL(
+          'ProjectItemUserResource/save?projectItemId=' + this.projectId,
+        ),
         type: 'post',
         contentType: 'application/json',
         data: JSON.stringify(userIds),
@@ -245,11 +236,42 @@ export default {
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
-
     },
   },
 };
 </script>
 
 <style scoped>
-</style>
+.user-container {
+  width: 100%;
+  margin-top: 10px;
+}
+
+.user-checkbox {
+  width: 100%;
+}
+
+.user-checkbox :deep(.ant-checkbox + span) {
+  width: 100%;
+}
+
+.ant-card {
+  cursor: pointer;
+  transition: all 0.3s;
+}
+h4 {
+  margin: 10px !important;
+  font-family: inherit;
+  font-weight: 500;
+  line-height: 1.1;
+  color: inherit;
+}
+
+:deep(.ant-card-body) {
+  padding: 12px !important;
+}
+
+.ant-card:hover {
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+</style>

+ 56 - 78
src/trace/TeamList.vue

@@ -1,36 +1,40 @@
 <template>
   <div>
     <TraceHeader :type="'team'" />
-    <div
-      id="trace-content"
-      class="container-fluid"
-    >
-      <div class="boxes">
-        <div
-          v-for="user in userList"
-          :key="user.id"
-          class="col-xs-6 col-sm-4 col-md-3 col-lg-2"
+
+    <a-row :gutter="[16, 24]" class="team-container">
+      <a-col
+        v-for="user in userList"
+        :key="user.id"
+        :xs="12"
+        :sm="8"
+        :md="6"
+        :lg="4"
+        :xl="4"
+        :xxs="24"
+      >
+        <a-card
+          hoverable
+          class="user-card"
+          @click="openUserNotFinishedTrace(user)"
         >
-          <a
-            class="box"
-            @click="openUserNotFinishedTrace(user)"
+          <a-avatar
+            :size="90"
+            class="user-avatar"
+            :style="{ backgroundColor: '#f6aa39' }"
           >
-            <span>&nbsp;</span>
-            <span class="box__statistics box__statistics--upcoming">
-              <span>{{ user.name.substring(user.name.length-1,user.name.length) }}</span>
-            </span>
-            <p class="box__title">{{ user.name }}</p>
-          </a>
-        </div>
-      </div>
-    </div>
+            {{ user.name.slice(-1) }}
+          </a-avatar>
+          <h3 class="user-name">{{ user.name }}</h3>
+        </a-card>
+      </a-col>
+    </a-row>
   </div>
 </template>
 
 <script>
 import Common from '../common/Common.js';
 
-
 import TraceHeader from './TraceHeader.vue';
 export default {
   components: {
@@ -45,11 +49,10 @@ export default {
     this.initData();
   },
   methods: {
-
     /**
-         * 初始化用户
-         * @author ZhangTeng 20190221
-         */
+     * 初始化用户
+     * @author ZhangTeng 20190221
+     */
     initData: function () {
       var _self = this;
       $.ajax({
@@ -70,72 +73,47 @@ export default {
     },
 
     /**
-         * 打开用户未完成的任务
-         * @author ZhangTeng 20190221
-         */
+     * 打开用户未完成的任务
+     * @author ZhangTeng 20190221
+     */
     openUserNotFinishedTrace: function (user) {
       this.$router.push({
         path: '/trace/userNotFinishedTrace/' + user.id,
         query: {
-          userName : user.name,
+          userName: user.name,
         },
       });
     },
-
   },
 };
 </script>
 
 <style scoped>
-.boxes .box {
-    display: block;
-    /* width: 90px; */
-    height: 235px;
-    border-radius: 4px;
-    text-align: center;
-    color: #000;
-    cursor: pointer;
-}
-.boxes .box__statistics {
-    display: block;
-    width: 90px;
-    height: 90px;
-    padding: 10px;
-    margin: 15px auto 0;
-    border-radius: 50%;
-    color: #fff;
-    font-size: 46px;
-}
-.boxes .box__statistics--upcoming {
-    background-color: #f6aa39;
+.team-container {
+  padding: 24px;
 }
-a {
-    text-decoration: none;
-    margin: 10px;
-    font-size: 100%;
-    vertical-align: baseline;
-    background: transparent;
-}
-.boxes .box__title {
-    margin-top: 25px;
-    font-size: 24px;
-}
-.boxes .box__title__weeks {
-    font-size: 18px;
+
+.user-card {
+  height: 235px;
+  border-radius: 4px;
+  text-align: center;
+  transition: all 0.3s;
 }
-.box:hover {
-    background-color: ghostwhite;
-    /* width: 90px; */
-    height: 235px;
+
+.user-card:hover {
+  background-color: #f8f9fa;
 }
-.row {
-    height: 200px;
-    margin-bottom: 10px;
+
+.user-avatar {
+  margin: 15px auto 0;
+  font-size: 36px;
+  line-height: 90px;
 }
-.dashboard-header {
-    cursor: pointer;
-    font-family: "\5FAE\8F6F\96C5\9ED1";
-    font-weight: bold;
-    font-size: 1.1em;
+
+.user-name {
+  margin-top: 25px;
+  font-size: 24px;
+  color: #000;
 }
-</style>s
+</style>
+s

+ 62 - 62
src/trace/TraceAttachment.vue

@@ -1,52 +1,58 @@
 <template>
   <div>
-    <div>
-      <input
-        ref="fileInput"
-        autocomplete="off"
-        type="file"
-        class="form-control file-input"
-        @change="onFileChanges"
-      />
-      <label for="attachment">
-        <a role="button" class="btn btn-primary btn-sm" @click="clickButton">
-          <i class="fa fa-upload" />
-          &nbsp; 上传附件
-        </a>
-      </label>
-    </div>
+    <a-space direction="vertical" style="width: 100%">
+      <!-- 上传附件按钮区域 -->
+      <a-upload
+        :file-list="[]"
+        :show-upload-list="false"
+        :before-upload="handleBeforeUpload"
+      >
+        <a-button type="primary" size="middle">
+          <upload-outlined />
+          上传附件
+        </a-button>
+      </a-upload>
 
-    <div v-if="attachmentList != undefined && attachmentList.length > 0">
-      <ul class="list-group">
-        <template v-for="attachment in attachmentList" :key="attachment.id">
-          <li class="list-group-item">
-            <span
-              class="badge"
+      <!-- 附件列表区域 -->
+      <a-list
+        v-if="attachmentList != undefined && attachmentList.length > 0"
+        bordered
+        size="small"
+      >
+        <a-list-item v-for="attachment in attachmentList" :key="attachment.id" class="attachment-list-item">
+          <div class="attachment-name">{{ attachment.attachment }}</div>
+          <template #actions>
+            <a-button type="link" size="small" @click="download(attachment)">
+              下载
+            </a-button>
+            <a-button 
+              type="link" 
+              size="small" 
+              danger 
               @click="deleteByTraceAttachmentId(attachment.id)"
-            >删除</span>
-            <span class="badge" @click="download(attachment)">下载</span>
-            {{ attachment.attachment }}
-          </li>
-        </template>
-      </ul>
-    </div>
+            >
+              删除
+            </a-button>
+          </template>
+        </a-list-item>
+      </a-list>
+    </a-space>
     <Loading v-if="loading" />
   </div>
 </template>
 
 <script>
 import Common from '../common/Common.js';
-
 import { DownloadService } from 'pc-component-v3';
 import { Notify } from 'pc-component-v3';
 import { message } from 'ant-design-vue';
+import { UploadOutlined } from '@ant-design/icons-vue';
 
 export default {
   components: {
-    
     DownloadService,
+    UploadOutlined,
   },
-  // props: ['traceId'],
   props: {
     traceId: {
       type: String,
@@ -77,6 +83,14 @@ export default {
     this.getTraceConfig();
   },
   methods: {
+    /**
+     * 在上传前处理文件
+     */
+    handleBeforeUpload(file) {
+      this.uploadFile(file);
+      return false; // 阻止自动上传
+    },
+
     /**
      * 下载文件
      * @param  {String} fileName 文件名
@@ -118,16 +132,6 @@ export default {
       });
     },
 
-    /* 选择文件发生改变
-     * @param  {[type]} e [description]
-     * @return {[type]}   [description]
-     */
-    onFileChanges: function (e) {
-      var files = e.target.files || e.dataTransfer.files;
-      if (!files.length) return;
-      this.uploadFile(files[0]);
-    },
-
     /**
      * 上传文件
      * @param  {File} selectedFile 选择的文件
@@ -256,33 +260,29 @@ export default {
         },
       });
     },
-
-    /**
-     * 点击上传按钮事件
-     * @return {[type]} [description]
-     */
-    clickButton: function () {
-      $(this.$refs.fileInput).click();
-    },
   },
 };
 </script>
 
 <style scoped>
-.badge {
-  cursor: pointer;
-  background-color: #428bca;
-  color: white;
+.ant-list-item {
+  padding: 8px 16px;
+}
+.attachment-list-item {
+  display: flex;
+  flex-wrap: wrap;
 }
-</style>
 
-<style scoped>
-.file-input {
-  width: 0.1px;
-  height: 0.1px;
-  opacity: 0;
-  overflow: hidden;
-  position: absolute;
-  z-index: -1;
+.attachment-name {
+  word-break: break-all;
+  overflow-wrap: break-word;
+  flex: 1;
+  min-width: 0;
+  padding-right: 16px;
+}
+
+:deep(.ant-list-item-action) {
+  margin-left: 0;
+  flex-shrink: 0;
 }
 </style>

+ 53 - 33
src/trace/TraceComment.vue

@@ -5,30 +5,45 @@
 -->
 
 <template>
-  <div>
-    <div v-for="items in traceComments" :key="items" class="media">
-      <h4 class="media-heading headDiv">
-        {{ items.createdName
-        }}<a class="fa-pull-right" @click="edit(items)">编辑</a>
-      </h4>
-      <div class="media-body">
-        <div>
-          <!-- eslint-disable-next-line -->
-          <div v-html="items.content" />
-          <br />
-          {{ items.created }}
-        </div>
-        <div v-if="items.attachments != '' && items.attachments != undefined">
-          <div
-            v-for="item in split(items.attachments)"
-            :key="item"
-            @click="download(item)"
-          >
-            <a>{{ item }}</a>
-          </div>
-        </div>
-      </div>
-    </div>
+  <div v-if="traceComments && traceComments.length">
+    <a-list :data-source="traceComments" :split="true">
+      <template #renderItem="{ item }">
+        <a-list-item>
+          <a-space direction="vertical" style="width: 100%">
+            <a-row align="middle">
+              <a-col :flex="1">
+                <a-typography-title :level="4" style="margin-bottom: 0">
+                  {{ item.createdName }}
+                </a-typography-title>
+              </a-col>
+              <a-col>
+                <a-button type="link" @click="edit(item)">编辑</a-button>
+              </a-col>
+            </a-row>
+
+            <div>
+              <!-- eslint-disable-next-line -->
+              <div id="content-editor-view" v-html="item.content" />
+              <a-typography-text type="secondary">
+                {{ item.created }}
+              </a-typography-text>
+            </div>
+
+            <div v-if="item.attachments != '' && item.attachments != undefined">
+              <a-space direction="vertical" style="width: 100%">
+                <a-typography-link
+                  v-for="attachment in split(item.attachments)"
+                  :key="attachment"
+                  @click="download(attachment)"
+                >
+                  {{ attachment }}
+                </a-typography-link>
+              </a-space>
+            </div>
+          </a-space>
+        </a-list-item>
+      </template>
+    </a-list>
   </div>
 </template>
 
@@ -37,15 +52,13 @@ import Common from '../common/Common.js';
 import { DownloadService } from 'pc-component-v3';
 export default {
   components: {
-    
     DownloadService,
   },
-  // props: ['traceId', 'trace'],
   props: {
     traceId: {
       type: String,
       default: null,
-    }, 
+    },
     trace: {
       type: String,
       default: null,
@@ -102,7 +115,7 @@ export default {
     },
 
     /**
-     * 将字符串以”,“进行分割成字符串
+     * 将字符串以","进行分割成字符串
      * @author GuoZhiBo 20171201
      */
     split: function (items) {
@@ -156,9 +169,16 @@ export default {
 </script>
 
 <style scoped>
-.headDiv{
-  display: flex;
-  justify-content: space-between;
-  font-size: 14px;
+:deep(p) {
+  margin: 0 0 10px !important;
+}
+
+:deep(.ant-list-item) {
+  padding: 12px !important;
+}
+
+:deep(.ant-typography) {
+  font-size: 16px !important;
+  margin: 6px 0 !important;
 }
-</style>
+</style>

+ 62 - 293
src/trace/TraceCommentCreate.vue

@@ -2,78 +2,72 @@
   <div>
     <div>
       <Navbar title="创建评论" :is-go-back="true" />
-      <div>
-        <div class="form-group">
-          <label for="exampleInputEmail2">评论内容</label>
-          <div id="summernote" />
-          <!-- <textarea style="width: 100%;"
-                              id="hcqk"
-                              class="form-control"
-                              rows="5"
-                              v-model="content"></textarea> -->
-        </div>
-        <div>
-          <input
-            ref="fileInput"
-            autocomplete="off"
-            type="file"
-            class="form-control file-input"
-            @change="onFileChanges"
-          />
-          <label for="attachment">
-            <a
-              role="button"
-              class="btn btn-primary btn-sm"
-              @click="clickButton"
+      <a-space direction="vertical" style="width: 100%">
+        <a-form layout="vertical">
+          <a-form-item label="评论内容">
+            <EditorWidget v-model="content" :class-name="className" />
+          </a-form-item>
+
+          <a-form-item>
+            <a-upload
+              :file-list="[]"
+              :show-upload-list="false"
+              :before-upload="handleBeforeUpload"
             >
-              <i class="fa fa-upload" />
-              &nbsp; 上传附件
-            </a>
-          </label>
-        </div>
+              <a-button type="primary">
+                <upload-outlined />
+                上传附件
+              </a-button>
+            </a-upload>
+          </a-form-item>
+
+          <a-form-item v-if="files.length > 0">
+            <a-list bordered size="small">
+              <a-list-item v-for="(item, index) in files" :key="item">
+                {{ item }}
+                <template #actions>
+                  <a-button type="link" size="small" @click="download(item)">
+                    下载
+                  </a-button>
+                  <a-button
+                    type="link"
+                    size="small"
+                    danger
+                    @click="removeFile(index)"
+                  >
+                    删除
+                  </a-button>
+                </template>
+              </a-list-item>
+            </a-list>
+          </a-form-item>
 
-        <div style="margin-top: 10px">
-          <ul class="list-group">
-            <li v-for="item in files" :key="item" class="list-group-item">
-              <span class="badge" @click="download(item)">下载</span>
-              <span class="badge" @click="removeFile(item)">删除</span>
-              {{ item }}
-            </li>
-          </ul>
-        </div>
-        <div>
-          <div style="margin-top: 10px">
-            <button type="text" class="btn btn-default" @click="photograph">
-              确认
-            </button>
-            <button type="text" class="btn btn-default" @click="back">
-              取消
-            </button>
-          </div>
-        </div>
-      </div>
+          <a-form-item>
+            <a-space>
+              <a-button type="primary" @click="photograph">确认</a-button>
+              <a-button @click="back">取消</a-button>
+            </a-space>
+          </a-form-item>
+        </a-form>
+      </a-space>
     </div>
     <Loading v-if="loading" />
   </div>
 </template>
 <script>
-
-
 import UpladFile from '../widget/UpladFile.js';
-
 import Common from '../common/Common.js';
 import { Notify } from 'pc-component-v3';
 import { DownloadService } from 'pc-component-v3';
 import { message } from 'ant-design-vue';
+import { UploadOutlined } from '@ant-design/icons-vue';
+import EditorWidget from '../widget/EditorWidget.vue';
 
 export default {
   components: {
-    
-    
     UpladFile,
-    
-    
-    
+    UploadOutlined,
+    EditorWidget,
   },
   data: function () {
     return {
@@ -89,200 +83,46 @@ export default {
   },
 
   mounted: function () {
-    var _self = this;
-    $('.input-tan').focus(function () {
-      document.activeElement.blur();
-    });
     this.initData();
     this.getTraceConfig();
-    $('#summernote').summernote({
-      toolbar: [
-        [
-          'insert',
-          [
-            'link',
-            'picture',
-            'video',
-            'audio',
-            'hr',
-            'table', //插件
-            'fontname',
-            'fontsize',
-            'color',
-            'bold',
-            'italic',
-            'underline',
-            'strikethrough',
-            'clear', //字体样式
-            'style',
-            'ol',
-            'ul',
-            'paragraph',
-            'height', //段落样式
-            'fullscreen',
-            'codeview',
-            'undo',
-            'redo',
-            'help', //Misc
-          ],
-        ],
-      ],
-      height: 400,
-      minHeight: 300,
-      maxHeight: 500,
-      focus: true,
-      lang: 'zh-CN',
-      callbacks: {
-        onImageUpload: function (files, editor, $editable) {
-          _self.sendFile(files[0], editor, $editable);
-        },
-      },
-    });
-    _self.summernoteInitSuccess = true;
-  },
-  beforeUnmount: function () {
-    if (this.summernoteInitSuccess == true) {
-      $('#summernote').summernote('destroy');
-    }
   },
   methods: {
     /**
-     * 点击上传按钮事件
-     * @return {[type]} [description]
+     * 在上传前处理文件
      */
-    clickButton: function () {
-      $(this.$refs.fileInput).click();
+    handleBeforeUpload: function (file) {
+      this.uploadFile(file);
+      return false; // 阻止自动上传
     },
+
     /**
      * 初始化数据
-     * @author GuoZhiBo 20171201
      */
     initData: function () {
       var _self = this;
       this.traceId = Number(this.$route.params.traceId);
     },
-    /**
-     * 监听文件改变事件
-     * @author GuoZhiBo 20171201
-     */
-    onFileChange(e) {
-      var _self = this;
-      var files = e.target.files || e.dataTransfer.files;
-      if (!files.length) return;
-      _self.uploadImage(files[0]);
-    },
-    /**
-     * 上传图片
-     * @author GuoZhiBo 20171201
-     */
-    uploadImage: function (selectedFile) {
-      var _self = this;
-      if (selectedFile == undefined) {
-        return;
-      }
-      if (!/image\/\w+/.test(selectedFile.type)) {
-        Notify.error('提示', '请确保文件为图像类型!');
-        return;
-      }
-      UpladFile.photoCompress(
-        selectedFile,
-        {
-          quality: 0.2,
-        },
-        function (base64Codes) {
-          var bl = UpladFile.convertBase64UrlToBlob(base64Codes);
-          var rst = new FormData();
-          rst.append('images', bl, 'file_' + Date.parse(new Date()) + '.jpg');
-          rst.append('className', 'com.leanwo.prodog.trace.model.TraceComment');
-          _self.loading=true;
-          $.ajax({
-            url: Common.getApiURL('file/imageUpload'),
-            type: 'post',
-            beforeSend: function (request) {
-              Common.addTokenToRequest(request);
-            },
-            data: rst,
-            contentType: false,
-            processData: false,
-            success: function (data) {
-              _self.loading=false;
-              if (data.errorCode == 0) {
-                if (data.datas) {
-                  var imageName = data.datas[0];
-                  _self.addImg(imageName);
-                }
-              } else {
-                message.error(data.errorMessage);
-              }
-            },
-            error: function (XMLHttpRequest, textStatus, errorThrown) {
-              _self.loading=false;
-              Common.processException(XMLHttpRequest, textStatus, errorThrown);
-            },
-          });
-          return rst.file;
-        },
-      );
-    },
     /**
      * 添加图片
-     * @author GuoZhiBo 20171201
      */
     addImg: function (imageName) {
       var _self = this;
       _self.images.push(imageName);
     },
-    /**
-     * 获取图片地址
-     * @param  {String} item 图片名称
-     * @return {String}      图片URL地址
-     */
-    getImageSrc: function (item) {
-      var _self = this;
-      if (item != undefined && item != null) {
-        return Common.getResourceUrl(
-          'image',
-          'com.leanwo.prodog.trace.model.TraceComment',
-          item,
-        );
-      } else {
-        return '';
-      }
-    },
-    /**
-     * 获取图片路径供外部调用
-     * @return {Array} 图片地址
-     */
-    getImages: function () {
-      return this.images;
-    },
     /**
      * 删除图片
-     *@author GuoZhiBo 20171201
      */
     deleteImg(index) {
       var _self = this;
       _self.images.splice(index, 1);
     },
-    //----------文件------------
     /**
      * 删除文件
-     * @param  {String} item 文件Name
      */
     removeFile: function (index) {
       var _self = this;
       _self.files.splice(index, 1);
     },
-    /**
-     * 选择文件发生改变
-     * @param  {[type]} e [description]
-     * @return {[type]}   [description]
-     */
-    onFileChanges: function (e) {
-      var files = e.target.files || e.dataTransfer.files;
-      if (!files.length) return;
-      this.uploadFile(files[0]);
-    },
 
     /**
      * 上传文件
@@ -309,7 +149,7 @@ export default {
           var formData = new FormData();
           formData.append('files', selectedFile);
           formData.append('className', _self.className);
-          _self.loading=true;
+          _self.loading = true;
           $.ajax({
             url: Common.getApiURL('file/classFileUpload'),
             type: 'post',
@@ -320,7 +160,7 @@ export default {
             contentType: false,
             processData: false,
             success: function (data) {
-              _self.loading=false;
+              _self.loading = false;
               if (data.errorCode == 0) {
                 if (data.datas) {
                   var fileName = data.datas[0];
@@ -329,10 +169,9 @@ export default {
               } else {
                 message.error(data.errorMessage);
               }
-
             },
             error: function (XMLHttpRequest, textStatus, errorThrown) {
-              _self.loading=false;
+              _self.loading = false;
               Common.processException(XMLHttpRequest, textStatus, errorThrown);
             },
           });
@@ -358,7 +197,6 @@ export default {
         beforeSend: function (request) {
           Common.addTokenToRequest(request);
         },
-
         success: function (data) {
           _self.traceConfigDto = data;
         },
@@ -367,19 +205,6 @@ export default {
         },
       });
     },
-    /**
-     * 获取文件地址
-     * @param  {String} item 图片名称
-     * @return {String}      图片URL地址
-     */
-    getFileSrc: function (item) {
-      var _self = this;
-      if (item != undefined && item != null) {
-        return Common.getResourceUrl('file', _self.className, item);
-      } else {
-        return '';
-      }
-    },
 
     /**
      * 下载文件
@@ -391,66 +216,18 @@ export default {
       DownloadService.fileDownload(_self.className, fileName);
     },
 
-    //内容图片上传
-    sendFile: function (file, editor, $editable) {
-      var _self = this;
-      UpladFile.photoCompress(
-        file,
-        {
-          quality: 1,
-        },
-        function (base64Codes) {
-          var bl = UpladFile.convertBase64UrlToBlob(base64Codes);
-          var rst = new FormData();
-          rst.append('images', bl, 'file_' + Date.parse(new Date()) + '.jpg');
-          rst.append('className', _self.className);
-          $.ajax({
-            url: Common.getApiURL('file/imageUpload'),
-            type: 'post',
-            data: rst,
-            contentType: false,
-            processData: false,
-            beforeSend: function (request) {
-              Common.addTokenToRequest(request);
-            },
-            success: function (data) {
-              if (data.errorCode == 0) {
-                if (data.datas) {
-                  var imageName = data.datas[0];
-                  $('#summernote').summernote(
-                    'insertImage',
-                    Common.getResourceUrl('image', _self.className, imageName),
-                    'image name',
-                  );
-                }
-              } else {
-                message.error(data.errorMessage);
-              }
-            },
-            error: function () {
-              Notify.error('提示', '上传失败!');
-              return;
-            },
-          });
-          return rst.file;
-        },
-      );
-    },
-
     /**
      * 保存评论表数据
-     * @author GuoZhiBo 20171201
      */
     photograph: function () {
       var _self = this;
-      _self.content = $('#summernote').summernote('code');
       if (_self.content != '' && _self.content != null) {
         var traceComment = {
           traceId: _self.traceId,
           content: _self.content,
           attachments: _self.files.join(','),
         };
-        _self.loading=true;
+        _self.loading = true;
         $.ajax({
           url: Common.getApiURL('TraceCommentResource/saveTraceComment'),
           type: 'post',
@@ -460,11 +237,11 @@ export default {
           },
           data: JSON.stringify(traceComment),
           success: function (data) {
-            _self.loading=false;
+            _self.loading = false;
             _self.back();
           },
           error: function (XMLHttpRequest, textStatus, errorThrown) {
-            _self.loading=false;
+            _self.loading = false;
             Common.processException(XMLHttpRequest, textStatus, errorThrown);
           },
         });
@@ -480,9 +257,6 @@ export default {
 };
 </script>
 <style scoped>
-.mui-divs3 {
-  margin-top: 10px;
-}
 .file-input {
   width: 0.1px;
   height: 0.1px;
@@ -491,9 +265,4 @@ export default {
   position: absolute;
   z-index: -1;
 }
-.badge {
-  cursor: pointer;
-  background-color: #428bca;
-  color: white;
-}
-</style>
+</style>

+ 134 - 213
src/trace/TraceCommentEdit.vue

@@ -2,140 +2,123 @@
   <div>
     <div>
       <Navbar title="修改评论" :is-go-back="true" />
-      <div>
-        <div class="form-group">
-          <label for="exampleInputEmail2">评论内容</label>
-          <div id="summernote" />
-          <!-- <textarea style="width: 100%;"
-                              id="hcqk"
-                              class="form-control"
-                              rows="5"
-                              v-model="traceComment.content"></textarea> -->
-        </div>
-        <div>
-          <input ref="fileInput" autocomplete="off" type="file" class="form-control file-input" @change="onFileChanges" />
-          <label for="attachment">
-            <a role="button" class="btn btn-primary btn-sm" @click="clickButton">
-              <i class="fa fa-upload" />
-              &nbsp;
-              上传附件
-            </a>
-          </label>
-        </div>
+      <a-space direction="vertical" style="width: 100%">
+        <a-form layout="vertical">
+          <a-form-item label="评论内容">
+            <EditorWidget
+              v-model="traceComment.content"
+              :class-name="className"
+            />
+          </a-form-item>
 
-        <div style="margin-top: 10px;">
-          <ul class="list-group">
-            <li v-for="item in files" :key="item" class="list-group-item">
-              <span class="badge" @click="download(item)">下载</span>
-              <span class="badge" @click="removeFile(item)">删除</span>
-              {{ item }}
-            </li>
-          </ul>
-        </div>
+          <a-form-item>
+            <a-upload
+              :file-list="[]"
+              :show-upload-list="false"
+              :before-upload="handleBeforeUpload"
+            >
+              <a-button type="primary">
+                <upload-outlined />
+                上传附件
+              </a-button>
+            </a-upload>
+          </a-form-item>
 
-        <div>
-          <div style="margin-top: 10px;">
-            <button type="text" class="btn btn-default" @click="photograph">确认</button>
-            <button type="text" class="btn btn-default" @click="back">取消</button>
-          </div>
-        </div>
-      </div>
+          <a-form-item v-if="files.length > 0">
+            <a-list bordered size="small">
+              <a-list-item v-for="(item, index) in files" :key="item">
+                {{ item }}
+                <template #actions>
+                  <a-button type="link" size="small" @click="download(item)">
+                    下载
+                  </a-button>
+                  <a-button
+                    type="link"
+                    size="small"
+                    danger
+                    @click="removeFile(index)"
+                  >
+                    删除
+                  </a-button>
+                </template>
+              </a-list-item>
+            </a-list>
+          </a-form-item>
+
+          <a-form-item>
+            <a-space>
+              <a-button type="primary" @click="photograph">确认</a-button>
+              <a-button @click="back">取消</a-button>
+            </a-space>
+          </a-form-item>
+        </a-form>
+      </a-space>
     </div>
     <Loading v-if="loading" />
   </div>
 </template>
 <script>
-
-
-import UpladFile from '../widget/UpladFile.js';
-
 import Common from '../common/Common.js';
 import { Notify } from 'pc-component-v3';
 import { DownloadService } from 'pc-component-v3';
 import { message } from 'ant-design-vue';
+import { UploadOutlined } from '@ant-design/icons-vue';
+import EditorWidget from '../widget/EditorWidget.vue';
 
 export default {
   components: {
-    
-    
-    UpladFile,
-    
-    
-    
+    EditorWidget,
+    UploadOutlined,
   },
-  data: function() {
+  data: function () {
     return {
       traceCommentId: '',
       traceComment: {},
       className: 'com.leanwo.prodog.trace.model.TraceComment',
-      summernoteInitSuccess: false, // summernote初始化成功
       files: [],
       loading: false,
     };
   },
-  mounted: function() {
-    $('.input-tan').focus(function() {
-      document.activeElement.blur();
-    });
+  mounted: function () {
     this.initData();
   },
   methods: {
-			
     /**
-			 * 点击上传按钮事
-			 * @return {[type]} [description]
-			 */
-    clickButton: function() {
-      $(this.$refs.fileInput).click();
+     * 处理上传前的文
+     */
+    handleBeforeUpload(file) {
+      this.uploadFile(file);
+      return false; // 阻止自动上传
     },
 
     /**
-			 * 初始化数据
-			 * @author GuoZhiBo 20171201
-			 */
-    initData: function() {
+     * 初始化数据
+     */
+    initData: function () {
       var _self = this;
       _self.traceCommentId = Number(this.$route.params.traceCommentId);
       if (_self.traceCommentId != null && _self.traceCommentId != '') {
-        _self.uniqueById(_self.traceCommentId).then(successData => {
-          _self.traceComment = successData;
-          if(successData.attachments != null && successData.attachments != ''){
-            _self.files=(successData.attachments.split(','));
-          }
-          $('#summernote').summernote({
-            toolbar:[
-              ['insert', ['link', 'picture', 'video', 'audio', 'hr', 'table', //插件
-                'fontname', 'fontsize', 'color', 'bold', 'italic', 'underline', 'strikethrough', 'clear',//字体样式
-                'style', 'ol', 'ul', 'paragraph', 'height',//段落样式
-                'fullscreen', 'codeview', 'undo', 'redo', 'help',//Misc
-              ]],
-            ],
-            height: 400,
-            minHeight: 300,
-            maxHeight: 500,
-            focus: true,
-            lang: 'zh-CN',
-            callbacks: {
-              onImageUpload: function(files, editor, $editable) {
-                _self.sendFile(files[0], editor, $editable);
-              },
-            },
-          });
-          if (_self.traceComment != null) {
-            $('#summernote').summernote('code', _self.traceComment.content);
-          }
-          _self.summernoteInitSuccess = true;
-        }, errorData => {
-          Common.processException(errorData);
-        });
+        _self.uniqueById(_self.traceCommentId).then(
+          successData => {
+            _self.traceComment = successData;
+            if (
+              successData.attachments != null &&
+              successData.attachments != ''
+            ) {
+              _self.files = successData.attachments.split(',');
+            }
+          },
+          errorData => {
+            Common.processException(errorData);
+          },
+        );
       }
-
     },
 
     /**
-			 * 查询Trace
-			 */
-    uniqueById: function(traceCommentId) {
+     * 查询Trace
+     */
+    uniqueById: function (traceCommentId) {
       return new Promise((resolve, reject) => {
         $.ajax({
           url: Common.getApiURL('TraceCommentResource/uniqueById'),
@@ -143,78 +126,77 @@ export default {
           dataType: 'json',
           contentType: 'application/json',
           data: {
-            'traceCommentId': traceCommentId,
+            traceCommentId: traceCommentId,
           },
-          beforeSend: function(request) {
+          beforeSend: function (request) {
             Common.addTokenToRequest(request);
           },
-          success: function(data) {
+          success: function (data) {
             resolve(data);
           },
-          error: function(XMLHttpRequest, textStatus, errorThrown) {
+          error: function (XMLHttpRequest, textStatus, errorThrown) {
             reject(XMLHttpRequest);
           },
         });
       });
     },
 
-    //----------文件------------
     /**
-			 * 删除文件
-			 * @param  {String} item 文件Name
-			 */
-    removeFile: function(index) {
+     * 删除文件
+     * @param  {Number} index 文件索引
+     */
+    removeFile: function (index) {
       var _self = this;
       _self.files.splice(index, 1);
     },
+
     /**
-			 * 选择文件发生改变
-			 * @param  {[type]} e [description]
-			 * @return {[type]}   [description]
-			 */
-    onFileChanges: function(e) {
+     * 选择文件发生改变
+     */
+    onFileChanges: function (e) {
       var files = e.target.files || e.dataTransfer.files;
-      if (!files.length)
-        return;
+      if (!files.length) return;
       this.uploadFile(files[0]);
     },
 
     /**
-			 * 上传文件
-			 * @param  {File} selectedFile 选择的文件
-			 */
-    uploadFile: function(selectedFile) {
+     * 上传文件
+     * @param  {File} selectedFile 选择的文件
+     */
+    uploadFile: function (selectedFile) {
       var _self = this;
       _self.showUploadDiv = false;
       if (selectedFile == undefined) {
         return;
       }
       if (selectedFile.size != undefined && selectedFile.size != null) {
-        if ((selectedFile.size / 1024) <= (1024 * 4)) {
+        if (selectedFile.size / 1024 <= 1024 * 4) {
           var formData = new FormData();
           formData.append('files', selectedFile);
           formData.append('className', _self.className);
-          _self.loading=true;
+          _self.loading = true;
           $.ajax({
             url: Common.getApiURL('file/classFileUpload'),
             type: 'post',
-            beforeSend: function(request) {
+            beforeSend: function (request) {
               Common.addTokenToRequest(request);
             },
             data: formData,
             contentType: false,
             processData: false,
-            success: function(data) {
-              _self.loading=false;
+            success: function (data) {
+              _self.loading = false;
               if (data.errorCode == 0) {
-                if(data.datas){
+                if (data.datas) {
                   var fileName = data.datas[0];
                   _self.files.push(fileName);
                 }
+              } else {
+                message.error(data.errorMessage);
               }
             },
-            error: function(XMLHttpRequest, textStatus, errorThrown) {
-              _self.loading=false;
+            error: function (XMLHttpRequest, textStatus, errorThrown) {
+              _self.loading = false;
               Common.processException(XMLHttpRequest, textStatus, errorThrown);
             },
           });
@@ -227,90 +209,40 @@ export default {
     },
 
     /**
-			 * 获取文件地址
-			 * @param  {String} item 图片名称
-			 * @return {String}      图片URL地址
-			 */
-    getFileSrc: function(item) {
-      var _self = this;
-      if (item != undefined && item != null) {
-        return Common.getResourceUrl('file', _self.className, item);
-      } else {
-        return '';
-      }
-    },
-
-    /**
-			 * 下载文件
-			 * @param  {String} fileName 文件名
-			 * @return {void}          
-			 */
-    download: function(fileName) {
+     * 下载文件
+     * @param  {String} fileName 文件名
+     * @return {void}
+     */
+    download: function (fileName) {
       var _self = this;
       DownloadService.fileDownload(_self.className, fileName);
     },
-    //图片上传
-    sendFile: function(file, editor, $editable) {
-      var _self = this;
-      UpladFile.photoCompress(file, {
-        quality: 0.2,
-      }, function(base64Codes) {
-        var bl = UpladFile.convertBase64UrlToBlob(base64Codes);
-        var rst = new FormData();
-        rst.append('images', bl, 'file_' + Date.parse(new Date()) + '.jpg');
-        rst.append('className', _self.className);
-        $.ajax({
-          url: Common.getApiURL('file/imageUpload'),
-          type: 'post',
-          data: rst,
-          contentType: false,
-          processData: false,
-          beforeSend : function(request){
-            Common.addTokenToRequest(request);
-          },
-          success: function(data) {
-            if (data.errorCode == 0) {
-              if (data.datas) {
-                var imageName = data.datas[0];
-                $('#summernote').summernote('insertImage', Common.getResourceUrl('image', _self.className, imageName), 'image name');
-              }
-            } else {
-              message.error(data.errorMessage);
-            }
-          },
-          error: function() {
-            Notify.error('提示', '上传失败!');
-            return;
-          },
-        });
-        return rst.file;
-      });
-    },
 
     /**
-			 * 修改并保存评论表数据
-			 * @author GuoZhiBo 20180302
-			 */
-    photograph: function() {
+     * 修改并保存评论表数据
+     */
+    photograph: function () {
       var _self = this;
-      _self.traceComment.content = $('#summernote').summernote('code');
-      if (_self.traceComment.content != '' && _self.traceComment.content != null) {
+      if (
+        _self.traceComment.content != '' &&
+        _self.traceComment.content != null
+      ) {
         _self.traceComment.attachments = _self.files.join(',');
-        _self.loading=true;
+        _self.loading = true;
         $.ajax({
           url: Common.getApiURL('TraceCommentResource/updateTraceComment'),
           type: 'post',
           contentType: 'application/json',
-          beforeSend: function(request) {
+          beforeSend: function (request) {
             Common.addTokenToRequest(request);
           },
           data: JSON.stringify(_self.traceComment),
-          success: function(data) {
-            _self.loading=false;
+          success: function (data) {
+            _self.loading = false;
             _self.back();
           },
-          error: function(XMLHttpRequest, textStatus, errorThrown) {
-            _self.loading=false;
+          error: function (XMLHttpRequest, textStatus, errorThrown) {
+            _self.loading = false;
             Common.processException(XMLHttpRequest, textStatus, errorThrown);
           },
         });
@@ -319,31 +251,20 @@ export default {
       }
     },
 
-    back: function() {
+    back: function () {
       var _self = this;
       history.back();
     },
-
   },
 };
 </script>
 <style scoped>
-	.mui-divs3 {
-		margin-top: 10px;
-	}
-
-	.file-input {
-		width: 0.1px;
-		height: 0.1px;
-		opacity: 0;
-		overflow: hidden;
-		position: absolute;
-		z-index: -1;
-	}
-
-	.badge {
-		cursor: pointer;
-		background-color: #428bca;
-		color: white;
-	}
+.file-input {
+  width: 0.1px;
+  height: 0.1px;
+  opacity: 0;
+  overflow: hidden;
+  position: absolute;
+  z-index: -1;
+}
 </style>

+ 41 - 34
src/trace/TraceConfig.vue

@@ -1,21 +1,35 @@
 <template>
-  <div class="container-fluid">
+  <div>
     <Navbar title="任务配置" :is-go-back="true" />
-    <div class="col-xs-12 form-horizontal">
-      <div class="form-group">
-        <label for="exampleInputEmail1">任务附件大小(M)</label>
-        <input v-model="traceConfigDto.traceAttachmentSize" autocomplete="off" class="form-control" type="number" placeholder="任务附件大小" />
-      </div>
-      <div class="form-group">
-        <label for="exampleInputPassword1">评论附件大小(M)</label>
-        <input v-model="traceConfigDto.commentAttachmentSize" autocomplete="off" class="form-control" placeholder="评论附件大小" />
-      </div>  
-      <div class="row">
-        <div class="col-md-12 button-right">
-          <button class="btn btn-primary" @click="saveTraceConfig">确定</button>
-        </div>
-      </div>
-    </div>
+    <a-form
+      :model="traceConfigDto"
+      layout="vertical"
+      class="trace-config-form"
+    >
+      <a-form-item label="任务附件大小(M)" name="traceAttachmentSize">
+        <a-input-number
+          v-model:value="traceConfigDto.traceAttachmentSize"
+          :min="0"
+          :step="1"
+          style="width: 100%;"
+          placeholder="任务附件大小"
+        />
+      </a-form-item>
+      <a-form-item label="评论附件大小(M)" name="commentAttachmentSize">
+        <a-input-number
+          v-model:value="traceConfigDto.commentAttachmentSize"
+          :min="0"
+          :step="1"
+          style="width: 100%;"
+          placeholder="评论附件大小"
+        />
+      </a-form-item>
+      <a-form-item>
+        <a-button type="primary" style="float: right;" @click="saveTraceConfig">
+          确定
+        </a-button>
+      </a-form-item>
+    </a-form>
   </div>
 </template>
 
@@ -24,28 +38,23 @@ import Common from '../common/Common.js';
 import { Notify } from 'pc-component-v3';
 
 export default {
-
   components: {
-    
-    
   },
   data: function() {
     return {
       traceConfigDto: {
-        traceAttachmentSize:0,
-        commentAttachmentSize:0,
+        traceAttachmentSize: 0,
+        commentAttachmentSize: 0,
       },
     };
   },
   mounted: function() {
     this.getTraceConfig();
   },
-
   methods: {
-
     /**
-			 * 获取任务管理配置
-			 */
+     * 获取任务管理配置
+     */
     getTraceConfig: function() {
       var _self = this;
       $.ajax({
@@ -55,7 +64,6 @@ export default {
         beforeSend: function(request) {
           Common.addTokenToRequest(request);
         },
-
         success: function(data) {
           _self.traceConfigDto = data;
         },
@@ -65,9 +73,9 @@ export default {
       });
     },
     /**
-			 * 保存任务管理配置
-			 */
-    saveTraceConfig:function(){
+     * 保存任务管理配置
+     */
+    saveTraceConfig: function() {
       var _self = this;
       $.ajax({
         url: Common.getApiURL('TraceConfigResource/saveOrUpdate'),
@@ -78,20 +86,19 @@ export default {
           Common.addTokenToRequest(request);
         },
         success: function(data) {
-          Notify.success('提示', '已保存!',1500);
+          Notify.success('提示', '已保存!', 1500);
         },
         error: function(XMLHttpRequest, textStatus, errorThrown) {
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
     },
-
   },
 };
 </script>
 
 <style scoped>
-	.button-right{
-		text-align: right;
-	}
+.trace-config-form {
+  max-width: 400px;
+}
 </style>

+ 86 - 199
src/trace/TraceCreate.vue

@@ -2,88 +2,82 @@
   <div>
     <div>
       <Navbar title="新建任务" :is-go-back="true" />
-      <div>
-        <div class="form-group">
-          <label for="exampleInputEmail1">标题</label>
-          <input
-            id="exampleInputEmail1"
-            v-model="traceDto.summary"
-            autocomplete="off"
-            type="text"
-            class="form-control"
-            placeholder="标题"
+      <a-space direction="vertical" style="width: 100%">
+        <a-form layout="vertical">
+          <a-form-item label="标题">
+            <a-input
+              v-model:value="traceDto.summary"
+              autocomplete="off"
+              placeholder="标题"
+            />
+          </a-form-item>
+
+          <a-form-item label="内容">
+            <EditorWidget v-model="traceDto.detail" :class-name="className" />
+          </a-form-item>
+
+          <AudioField
+            :class-name="className"
+            :src-array="srcArray"
+            :is-readonly="'false'"
+            @handle-stop="handelEndRecord"
+            @delete-audio-src="deleteAudioSrc"
           />
-        </div>
-        <div class="form-group">
-          <label for="exampleInputPassword1">内容</label>
-          <div id="summernote" />
-        </div>
-        <AudioField
-          :class-name="className"
-          :src-array="srcArray"
-          :is-readonly="'false'"
-          @handle-stop="handelEndRecord"
-          @delete-audio-src="deleteAudioSrc"
-        />
-        <div class="form-group">
-          <label for="trackUserId">责任人</label>
-          <select id="trackUserId" v-model="userSelected" class="form-control">
-            <option value="" />
-            <option
-              v-for="user in projectUserList"
-              :key="user"
-              :value="user.userId"
+
+          <a-form-item label="责任人">
+            <a-select
+              v-model:value="userSelected"
+              placeholder="请选择责任人"
+              style="width: 100%"
             >
-              {{ user.userName }}
-            </option>
-          </select>
-        </div>
-        <div class="form-group">
-          <label for="projectName">项目</label>
-          <select
-            id="projectName"
-            v-model="projectId"
-            class="form-control"
-            :disabled="projectId != null ? true : false"
-          >
-            <option
-              v-for="project in projectUserList"
-              :key="project"
-              :value="project.projectItemId"
+              <a-select-option value="" />
+              <a-select-option
+                v-for="user in projectUserList"
+                :key="user.userId"
+                :value="user.userId"
+              >
+                {{ user.userName }}
+              </a-select-option>
+            </a-select>
+          </a-form-item>
+
+          <a-form-item label="项目">
+            <a-select
+              v-model:value="projectId"
+              placeholder="请选择项目"
+              style="width: 100%"
+              :disabled="projectId != null ? true : false"
             >
-              {{ project.projectItemName }}
-            </option>
-          </select>
-        </div>
-        <div class="form-group">
-          <label for="projectName">时间节点</label>
-          <ATreeselect
-            ref="treeSelect"
-            :multiple="false"
-            :options="projectTaskDtos"
-          />
-        </div>
-        <div class="form-group">
-          <label>完成时间</label><br />
-          <!-- <a-config-provider :locale="locale"> -->
-          <a-date-picker
-            v-model:value="planFinishedDate"
-            style="width: 100%"
-            :show-today="false"
-            placeholder="请选择完成日期"
-            @change="getDate"
-          />
-          <!-- </a-config-provider> -->
-        </div>
-        <button
-          style="width: 100%; background-color: #007aff; color: white"
-          type="button"
-          class="btn btn-blue search-button"
-          @click="saveTrace()"
-        >
-          保存
-        </button>
-      </div>
+              <a-select-option :value="projectId">
+                {{ projectName }}
+              </a-select-option>
+            </a-select>
+          </a-form-item>
+
+          <a-form-item label="时间节点">
+            <ATreeselect
+              ref="treeSelect"
+              :multiple="false"
+              :options="projectTaskDtos"
+            />
+          </a-form-item>
+
+          <a-form-item label="完成时间">
+            <a-date-picker
+              v-model:value="planFinishedDate"
+              style="width: 100%"
+              placeholder="请选择完成日期"
+              @change="getDate"
+            />
+          </a-form-item>
+
+          <a-form-item>
+            <a-button type="primary" block @click="saveTrace()">
+              保存
+            </a-button>
+          </a-form-item>
+        </a-form>
+      </a-space>
     </div>
     <Loading v-if="loading" />
   </div>
@@ -91,19 +85,17 @@
 <script type="text/javascript">
 import Common from '../common/Common.js';
 
-import UpladFile from '../widget/UpladFile.js';
 import { Notify } from 'pc-component-v3';
 import Treeselect from '../widget/TreeSelect.vue';
 import ATreeselect from '../widget/ATreeSelect.vue';
 import AudioField from '../widget/AudioField.vue';
-import { message } from 'ant-design-vue';
+import EditorWidget from '../widget/EditorWidget.vue';
 export default {
   components: {
-    UpladFile,
-
     Treeselect,
     ATreeselect,
     AudioField,
+    EditorWidget,
   },
   data: function () {
     return {
@@ -115,10 +107,10 @@ export default {
       planFinishedDate: '',
       isdisable: false, //日期框是否可编辑
       projectId: '', //项目Id
-      summernoteInitSuccess: false, // summernote初始化成功
       projectTaskDtos: [], //项目任务时间点
       srcArray: [],
       loading: false,
+      projectName: '',
     };
   },
   watch: {
@@ -132,61 +124,7 @@ export default {
   mounted: function () {
     var _self = this;
     this.projectId = this.$route.params.projectId;
-    console.log(this.projectId, 'projectId');
     this.initProject();
-    // this.initData();
-    $('#summernote').summernote({
-      toolbar: [
-        [
-          'insert',
-          [
-            'link',
-            'picture',
-            'video',
-            'audio',
-            'hr',
-            'table', //插件
-            'fontname',
-            'fontsize',
-            'color',
-            'bold',
-            'italic',
-            'underline',
-            'strikethrough',
-            'clear', //字体样式
-            'style',
-            'ol',
-            'ul',
-            'paragraph',
-            'height', //段落样式
-            'fullscreen',
-            'codeview',
-            'undo',
-            'redo',
-            'help', //Misc
-          ],
-        ],
-      ],
-      height: 400,
-      minHeight: 300,
-      maxHeight: 500,
-      focus: true,
-      lang: 'zh-CN',
-      callbacks: {
-        onImageUpload: function (files, editor, $editable) {
-          _self.sendFile(files[0], editor, $editable);
-        },
-      },
-    });
-    _self.summernoteInitSuccess = true;
-  },
-
-  beforeUnmount: function () {
-    if (this.summernoteInitSuccess == true) {
-      $('#summernote').summernote('destroy');
-    }
-    //界面销毁时,修改插入连接的display属性
-    $('.note-link-popover').removeAttr('style');
   },
 
   methods: {
@@ -213,8 +151,8 @@ export default {
     back: function () {
       history.back();
     },
-    getDate:function (_,dateStr) {
-      const _self = this;  
+    getDate: function (_, dateStr) {
+      const _self = this;
       if (_self.planFinishedDate != undefined && _self.planFinishedDate != '') {
         _self.traceDto.planFinishedDate = dateStr + ' 00:00:00'; //完成时间
       }
@@ -223,14 +161,8 @@ export default {
       var _self = this;
       _self.traceDto.receiveUserId = _self.userSelected; //接收人Id
       _self.traceDto.projectTaskId = _self.$refs.treeSelect.getValue();
-      // if (_self.planFinishedDate != undefined && _self.planFinishedDate != '') {
-      //   _self.traceDto.planFinishedDate = _self.dateConvert(
-      //     _self.planFinishedDate,
-      //   ) + ' 00:00:00'; //完成时间
-      // }
-      _self.traceDto.projectId = _self.projectId; //项目Id
 
-      _self.traceDto.detail = $('#summernote').summernote('code');
+      _self.traceDto.projectId = _self.projectId; //项目Id
       if (_self.traceDto.summary != '' && _self.traceDto.summary != undefined) {
         _self.loading = true;
         $.ajax({
@@ -257,52 +189,6 @@ export default {
       }
     },
 
-    //图片上传
-    sendFile: function (file, editor, $editable) {
-      var _self = this;
-      UpladFile.photoCompress(
-        file,
-        {
-          quality: 1,
-        },
-        function (base64Codes) {
-          var bl = UpladFile.convertBase64UrlToBlob(base64Codes);
-          var rst = new FormData();
-          rst.append('images', bl, 'file_' + Date.parse(new Date()) + '.jpg');
-          rst.append('className', _self.className);
-          $.ajax({
-            url: Common.getApiURL('file/imageUpload'),
-            type: 'post',
-            data: rst,
-            contentType: false,
-            processData: false,
-            beforeSend: function (request) {
-              Common.addTokenToRequest(request);
-            },
-            success: function (data) {
-              if(data.errorCode == 0){
-                if (data.datas) {
-                  var imageName = data.datas[0];
-                  $('#summernote').summernote(
-                    'insertImage',
-                    Common.getResourceUrl('image', _self.className, imageName),
-                    'image name',
-                  );
-                }
-              }else{
-                message.error(data.errorMessage);
-              }
-            },
-            error: function () {
-              Notify.error('提示', '上传失败!');
-              return;
-            },
-          });
-          return rst.file;
-        },
-      );
-    },
-
     /**
      * 初始化人员
      * @author ZhangTeng 20190213
@@ -319,7 +205,6 @@ export default {
           Common.addTokenToRequest(request);
         },
         success: function (data) {
-          console.log(data, '人员');
           if (data != null) {
             for (var index = 0; index < data.length; index++) {
               _self.userList[index] = data[index];
@@ -353,11 +238,12 @@ export default {
           Common.addTokenToRequest(request);
         },
         success: function (data) {
-          console.log(data, '项目');
           if (data != null) {
             for (var index = 0; index < data.length; index++) {
               _self.projectUserList[index] = data[index];
-              // _self.$set(_self.projectUserList, index, data[index]);
+              if (data[index].projectItemId == _self.projectId) {
+                _self.projectName = data[index].projectItemName;
+              }
             }
           }
         },
@@ -398,7 +284,6 @@ export default {
           Common.addTokenToRequest(request);
         },
         success: function (data) {
-          console.log(data, '时间节点');
           _self.projectTaskDtos = _self.convertToTreedata(data);
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
@@ -408,7 +293,6 @@ export default {
     },
 
     convertToTreedata: function (data) {
-      // console.log(data);
       var returnData = [];
       for (var i = 0; i < data.length; i++) {
         var tempObj = {
@@ -456,4 +340,7 @@ export default {
 </script>
 
 <style scoped>
-</style>
+:deep(.ant-form-item){
+  margin-bottom: 8px !important;
+}
+</style>

+ 145 - 168
src/trace/TraceDynamic.vue

@@ -2,108 +2,105 @@
   <div>
     <TraceHeader :type="'traceDynamic'" />
 
-    <div class="form-inline">
-      <div class="form-group">
-        <label for="exampleInputName2">任务内容</label>
-        <input
-          id="exampleInputName2"
-          v-model="searchText"
-          autocomplete="off"
-          type="text"
-          class="form-control"
+    <a-row :gutter="16" class="filter-form">
+      <a-col :xs="24" :sm="12" :md="10" :lg="8" :xl="8" :xxl="4" :xxs="24">
+        <label>任务名称 </label>
+        <a-input
+          v-model:value="searchText"
           placeholder="任务名称"
-          style="width: 200px"
+          style="width: 240px"
           @keyup.enter="query"
         />
-      </div>
-      <div class="form-group">
-        <label for="exampleInputEmail2">项目</label>
-        <div class="input-group">
+      </a-col>
+
+      <!-- <a-col :span="4">
+        <a-input-group>
+          <template #prefix>项目</template>
           <VueSimpleSuggest
             v-model="selectedProjectName"
             :list="projects"
             :filter-by-query="true"
-            :styles="suggestStyles"
             :min-length="-1"
             display-attribute="name"
             value-attribute="id"
             @suggestion-click="selectProject"
           />
-        </div>
-      </div>
-      <div class="form-group">
-        <label for="exampleInputEmail2">用户</label>
-        <div class="input-group">
+        </a-input-group>
+      </a-col>
+
+      <a-col :span="4">
+        <a-input-group>
+          <template #prefix>用户</template>
           <VueSimpleSuggest
             v-model="selectedUserName"
             :list="users"
             :filter-by-query="true"
-            :styles="suggestStyles"
             :min-length="-1"
             display-attribute="name"
             value-attribute="id"
             @suggestion-click="selectUser"
           />
-        </div>
-      </div>
-      <button
-        class="btn btn-default"
-        @click="query"
-      >
-        查询
-      </button>
-    </div>
-
-    <div class="row">
-      <div class="col-sm-12">
-        <div
-          v-for="(items,key) in traceLogMap"
-          :key="key"
-        >
+        </a-input-group>
+      </a-col> -->
+
+      <a-col :xs="24" :sm="12" :md="10" :lg="8" :xl="8" :xxl="4" :xxs="24">
+        <a-button type="primary" @click="query">查询</a-button>
+      </a-col>
+    </a-row>
+    <a-alert v-if="Object.keys(traceLogMap).length == 0" style="width: 100%;" message="暂无任务动态" type="warning" />
+    <a-row>
+      <a-col v-if="Object.keys(traceLogMap).length > 0" :span="24">
+        <div v-for="(items, key) in traceLogMap" :key="key">
           <h4>{{ getDateFormat(key) }}</h4>
+          <a-divider />
 
-          <hr />
-
-          <ul class="list-group">
-            <li
-              v-for="item in items"
-              :key="item.id"
-              class="list-group-item"
-              @click="openLineTrace(item.traceId)"
-            >
-              <span class="badge">{{ formatDate(item.dateTime) }}</span>
-              <span style="color: #000000;">{{ item.projectName }}&nbsp;</span>
-              {{ item.operatorUserName }}&nbsp;
-              {{ getContent(item.content) }} {{ item.traceContent }}
-            </li>
-          </ul>
+          <a-list item-layout="horizontal" :data-source="items">
+            <template #renderItem="{ item }">
+              <a-list-item @click="openLineTrace(item.traceId)">
+                <a-list-item-meta>
+                  <template #title>
+                    <div style="display: flex; align-items: center">
+                      {{ item.projectName }}&nbsp;{{
+                        item.operatorUserName
+                      }}&nbsp; {{ getContent(item.content) }}
+                      {{ item.traceContent }}
+                    </div>
+                    <a-tag color="#777">{{ formatDate(item.dateTime) }}</a-tag>
+                  </template>
+                </a-list-item-meta>
+              </a-list-item>
+            </template>
+          </a-list>
+          <a-divider />
         </div>
-      </div>
-      <div class="col-sm-12">
-        <VueBootstrapPagination
-          :pagination="pagination"
-          :callback="getDatas"
-          @click="updateRouter"
-        />
-      </div>
-    </div>
+      </a-col>
+
+      <a-col v-show="Object.keys(traceLogMap).length > 0" :span="24">
+        <div style="margin: 16px 0">
+          <AntdPagination
+            ref="paginationRef"
+            :pagination="pagination"
+            @get-page-params="getPageParams"
+          />
+        </div>
+      </a-col>
+    </a-row>
+    <Loading v-if="loading" />
   </div>
 </template>
 
 <script>
-
 import Common from '../common/Common.js';
 
 import TraceCommon from './TraceCommon.js';
 
 import TraceHeader from './TraceHeader.vue';
-;
 import { Notify } from 'pc-component-v3';
 // import VueSimpleSuggest from 'vue-simple-suggest';
 
 export default {
   components: {
-    TraceHeader, 
+    TraceHeader,
     // VueSimpleSuggest,
   },
   data: function () {
@@ -117,15 +114,16 @@ export default {
       projects: [],
       pagination: {
         total: 0,
-        per_page: Common.pageSize,    // required
+        per_page: Common.pageSize, // required
         current_page: 1, // required
-        last_page: 10,    // required
+        last_page: 10, // required
       },
       traceLogMap: {},
       formatDate: TraceCommon.formatDate,
       suggestStyles: {
-        'defaultInput': 'form-control',
+        defaultInput: 'form-control',
       },
+      loading: false,
     };
   },
 
@@ -159,17 +157,10 @@ export default {
 
   methods: {
     query: function () {
-      this.pagination.total = 0;
-      this.pagination.per_page = Common.pageSize;   // required
-      this.pagination.current_page = 1; // required
-      this.pagination.last_page = 10;    // required
-      this.pagination.from = 1;
-      this.pagination.to = 10;           // required
-      this.getDatas();
+      this.$refs.paginationRef.backFirstPage();
     },
 
     updateRouter: function () {
-      // var uuid = Uuid.createUUID();
       this.$router.push({
         path: '/trace/traceDynamic/',
         query: {
@@ -177,28 +168,34 @@ export default {
           pageSize: this.pagination.per_page,
         },
       });
-      console.log(this.$route);
-      // console.log(this.$router.query.currentPage);
-      // console.log(this.$router.query.pageSize);
       this.getDatas();
     },
 
+    // 页数改变
+    getPageParams: function (page, pageSize) {
+      this.pagination.current_page = page;
+      this.pagination.per_page = pageSize;
+      this.updateRouter();
+    },
+
     /**
-         * 查询数据
-         * @param {Object} success
-         * @param {Object} error
-         */
+     * 查询数据
+     * @param {Object} success
+     * @param {Object} error
+     */
     getDatas: function () {
       var _self = this;
       var queryParam = {
         range: {
-          start: (_self.pagination.current_page - 1) * _self.pagination.per_page,
+          start:
+            (_self.pagination.current_page - 1) * _self.pagination.per_page,
           length: _self.pagination.per_page,
         },
         condition: _self.searchText,
         projectId: _self.selectedProjectId,
         operatorUserId: _self.selectedUserId,
       };
+      _self.loading = true;
       $.ajax({
         url: Common.getApiURL('TraceLogResource/queryByCondition'),
         type: 'post',
@@ -210,19 +207,23 @@ export default {
         },
         success: function (data) {
           _self.traceLogMap = data.map;
-                   
+
           _self.pagination.total = data.totalSize;
-          _self.pagination.last_page = Math.ceil(data.totalSize / data.range.length);
+          _self.pagination.last_page = Math.ceil(
+            data.totalSize / data.range.length,
+          );
+          _self.loading = false;
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
+          _self.loading = false;
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
     },
 
     /**
-         * 选择了项目
-         */
+     * 选择了项目
+     */
     selectProject: function (project) {
       if (project) {
         this.selectedProjectId = project.id;
@@ -231,8 +232,8 @@ export default {
       }
     },
     /**
-         * 选择了用户
-         */
+     * 选择了用户
+     */
     selectUser: function (user) {
       if (user) {
         this.selectedUserId = user.id;
@@ -242,9 +243,9 @@ export default {
     },
 
     /**
-         * 打开明细
-         * @author GuoZhiBo 20180116
-         */
+     * 打开明细
+     * @author GuoZhiBo 20180116
+     */
     openLineTrace: function (traceId) {
       var _self = this;
       if (traceId != null && traceId != undefined) {
@@ -257,8 +258,8 @@ export default {
     },
 
     /**
-         * 获取日期格式
-         */
+     * 获取日期格式
+     */
     getDateFormat: function (date1) {
       var _self = this;
       if (date1 == undefined && date1 == '') {
@@ -277,8 +278,8 @@ export default {
     },
 
     /**
-         * 获取当前日期
-         */
+     * 获取当前日期
+     */
     getNowFormatDate: function () {
       var date = new Date();
       var seperator1 = '-';
@@ -296,14 +297,13 @@ export default {
     },
 
     /**
-         * 计算时间差
-         * @param {Object} sDate1
-         * @param {Object} sDate2
-         */
-    dateDifference: function (sDate1, sDate2) {    //sDate1和sDate2是2006-12-18格式  
-      var dateSpan,
-        tempDate,
-        iDays;
+     * 计算时间差
+     * @param {Object} sDate1
+     * @param {Object} sDate2
+     */
+    dateDifference: function (sDate1, sDate2) {
+      //sDate1和sDate2是2006-12-18格式
+      var dateSpan, tempDate, iDays;
       sDate1 = Date.parse(sDate1);
       sDate2 = Date.parse(sDate2);
       dateSpan = sDate2 - sDate1;
@@ -313,9 +313,9 @@ export default {
     },
 
     /**
-         * 获取日期
-         * @param {Object} dateString
-         */
+     * 获取日期
+     * @param {Object} dateString
+     */
     convertDateFromString: function (dateString) {
       if (dateString) {
         var date = new Date(dateString.replace(/-/, '/'));
@@ -325,8 +325,8 @@ export default {
     },
 
     /**
-         * 初始化项目
-         */
+     * 初始化项目
+     */
     initProject: function () {
       var _self = this;
       $.ajax({
@@ -351,8 +351,8 @@ export default {
     },
 
     /**
-         * 初始化用户
-         */
+     * 初始化用户
+     */
     initUser: function () {
       var _self = this;
       $.ajax({
@@ -376,12 +376,12 @@ export default {
       });
     },
 
-    getContent: function(content){
-      if(content == null){
+    getContent: function (content) {
+      if (content == null) {
         return '';
       }
-      var result = content.replace(/<[^>]+>/g,'');
-      result = result.replace(/&nbsp;/ig, '');
+      var result = content.replace(/<[^>]+>/g, '');
+      result = result.replace(/&nbsp;/gi, '');
       return result;
     },
   },
@@ -389,64 +389,41 @@ export default {
 </script>
 
 <style scoped>
-.mui-card {
-    margin: 0px;
-    margin-top: 0px;
-}
-.mui-card-footer:before,
-.mui-card-header:after {
-    background-color: #ffffff;
-}
-.mui-card-content {
-    padding: 0px 10px;
-}
-.mui-card-content p {
-    margin: 0px;
-}
-p {
-    font-size: 14px !important;
-    padding: 2px 0px;
-    color: #000000;
-}
-.time {
-    /*color: #8f8f94;*/
-    font-weight: bold;
-}
-.index {
-    font-size: 16px;
-    font-weight: bold;
+.filter-form {
+  margin: 10px 10px 0 10px;
 }
-.div-statis {
-    margin-bottom: 5px;
-    font-size: 0.8em;
-    text-align: center;
-}
-.mui-card-header {
-    padding-bottom: 0px;
+
+.ant-input-group-addon:first-child {
+  min-width: 60px;
 }
-.mui-div {
-    word-wrap: break-word;
-    white-space: normal;
+
+.ant-list-item {
+  cursor: pointer;
+  transition: all 0.3s;
 }
-.mui-div1 {
-    padding-left: 8%;
-    color: blue;
+
+.ant-list-item:hover {
+  background-color: #fafafa;
 }
-.mui-div2 {
-    color: cadetblue;
+
+:deep(.ant-divider-horizontal) {
+  margin: 6px 0 !important;
 }
 
-.mui-divs {
-    width: 50px;
-    height: 50px;
-    line-height: 44px;
-    text-align: center;
-    border-radius: 50%;
-    border: 4px solid #d2e8ea;
-    float: left;
-    margin-top: -3%;
+:deep(.ant-list-item) {
+  padding: 0 !important;
 }
-img {
-    width: 50px;
+
+:deep(.ant-list-item-meta-title) {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin: 0 !important;
+  padding: 10px 15px !important;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-size: 14px !important;
+  line-height: 1.42857143 !important;
+  color: #333 !important;
+  font-weight: 500 !important;
 }
-</style>
+</style>

+ 42 - 57
src/trace/TraceHeader.vue

@@ -1,64 +1,27 @@
 <template>
-  <nav class="navbar navbar-default">
-    <div class="container-fluid">
-      <div class="navbar-header">
-        <button
-          type="button"
-          class="navbar-toggle collapsed"
-          data-toggle="collapse"
-          data-target="#bs-example-navbar-collapse-1"
-          aria-expanded="false"
-        >
-          <span class="sr-only">Toggle navigation</span>
-          <span class="icon-bar" />
-          <span class="icon-bar" />
-          <span class="icon-bar" />
-        </button>
-        <a
-          class="navbar-brand"
-          href="javascript:void(0)"
-          style="padding-top: 6px"
-        >
-          <span
-            class="glyphicon glyphicon-circle-arrow-left m-image"
-            style="font-size: 36px; color: black"
-            @click="goBack"
-          />
-        </a>
-      </div>
-
-      <div id="bs-example-navbar-collapse-1" class="collapse navbar-collapse">
-        <ul class="nav navbar-nav">
-          <li :class="{ active: type == 'traceProject' }" @click="openProject">
-            <a class="nav-item-a" href="javascript:void(0)">项目</a>
-          </li>
-          <li :class="{ active: type == 'traceList' }" @click="openSelf">
-            <a class="nav-item-a" href="javascript:void(0)">我自己</a>
-          </li>
-          <li :class="{ active: type == 'traceDynamic' }" @click="openDynamic">
-            <a class="nav-item-a" href="javascript:void(0)">动态</a>
-          </li>
-          <li :class="{ active: type == 'team' }" @click="team">
-            <a class="nav-item-a" href="javascript:void(0)">团队</a>
-          </li>
-          <!-- <li @click="goHome">
-            <a class="nav-item-a" href="javascript:void(0)">主页</a>
-          </li> -->
-          <li v-if="isShow" @click="traceSetting">
-            <a class="nav-item-a" href="javascript:void(0)">任务配置</a>
-          </li>
-        </ul>
-      </div>
-    </div>
-  </nav>
+  <a-layout-header class="header">
+    <a-row align="middle">
+      <LeftCircleOutlined class="back-icon" @click="goBack" />
+      <a-menu v-model:selectedKeys="selectedKeys" class="menu-list" mode="horizontal" @click="handleMenuClick">
+        <a-menu-item key="traceProject">项目</a-menu-item>
+        <a-menu-item key="traceList">我自己</a-menu-item>
+        <a-menu-item key="traceDynamic">动态</a-menu-item>
+        <a-menu-item v-if="isShow" key="team">团队</a-menu-item>
+        <a-menu-item v-if="isShow" key="traceSetting">任务配置</a-menu-item>
+      </a-menu>
+    </a-row>
+  </a-layout-header>
 </template>
 
 <script>
 
 import Common from '../common/Common.js';
+import { LeftCircleOutlined } from '@ant-design/icons-vue';
 
 export default {
-  // props: ['type'],
+  components: {
+    LeftCircleOutlined,
+  },
   props: {
     type: {
       type: String,
@@ -68,7 +31,8 @@ export default {
 
   data: function () {
     return {
-      isShow: 'false',
+      isShow: false,
+      selectedKeys: [this.type],
     };
   },
   mounted: function () {
@@ -76,6 +40,15 @@ export default {
   },
 
   methods: {
+    handleMenuClick({ key }) {
+      switch (key) {
+      case 'traceProject': return this.openProject();
+      case 'traceList': return this.openSelf();
+      case 'traceDynamic': return this.openDynamic();
+      case 'team': return this.team();
+      case 'traceSetting': return this.traceSetting();
+      }
+    },
     /**
      * 打开项目界面
      */
@@ -152,12 +125,24 @@ export default {
         },
       });
     },
+    // 查询是否有查看团队权限
   },
 };
 </script>
 <style scoped>
-.nav-item-a {
-  padding-top: 15px;
-  padding-bottom: 15px;
+.header {
+  width: 100%;
+  background: #fff;
+  padding: 0;
+  box-shadow: 0 2px 8px #f0f1f2;
+}
+
+.menu-list {
+  padding-bottom: 4px;
+  width: 600px;
+}
+
+.back-icon {
+  font-size: 30px;
 }
 </style>

+ 190 - 166
src/trace/TraceList.vue

@@ -3,70 +3,59 @@
     <TraceHeader :type="'traceList'" />
     <div>
       <div>
-        <ul class="nav nav-tabs">
-          <li :class="{'active' : param.traceUserStatus == 'I_LAUNCH'}" @click="param.traceUserStatus='I_LAUNCH'"><a>我负责的</a></li>
-          <li :class="{'active' : param.traceUserStatus == 'I_ATTEND_PROJECT_ALL'}" @click="param.traceUserStatus='I_ATTEND_PROJECT_ALL'"><a>我参与项目的</a></li>
-          <li :class="{'active' : param.traceUserStatus == 'I_RESPONSE'}" @click="param.traceUserStatus='I_RESPONSE'"><a>我发起的</a></li>
-        </ul>
+        <a-tabs v-model:activeKey="param.traceUserStatus" @change="query">
+          <a-tab-pane key="I_LAUNCH" tab="我负责的" />
+          <a-tab-pane key="I_ATTEND_PROJECT_ALL" tab="我参与项目的" />
+          <a-tab-pane key="I_RESPONSE" tab="我发起的" />
+        </a-tabs>
       </div>
-      <br />
-      <div class="form-group">
+      <div>
         <label for="exampleInputEmail2">选择任务状态</label>
-        <select v-model="param.traceStatus" class="form-control">
-          <option value="RUNNING">追踪中</option>
-          <option value="FINISHED">完成的</option>
-        </select>
+        <a-select v-model:value="param.traceStatus" style="width: 100%" @change="query">
+          <a-select-option value="RUNNING">追踪中</a-select-option>
+          <a-select-option value="FINISHED">完成的</a-select-option>
+        </a-select>
       </div>
       <div class="form-group">
         <label for="exampleInputEmail2">查询条件</label>
-        <QueryWidget ref="queryWidget" @search="getDatas()" @value-changed="getDatas()" />
+        <QueryWidget ref="queryWidget" @search="query()" @value-changed="query()" />
       </div>
-    </div>
+      <a-alert v-if="traceDtos.length === 0" style="width: 100%;margin-top: 10px;" message="暂无任务" type="warning" />
 
-    <div class="panel panel-default">
-      <div class="panel-body">
+
+      <div v-else class="form-group list-box">
         <vuedraggable v-model="traceDtos" :disabled="true" item-key="id" class="wrapper" @change="end">
-          <template #item="{element}">
-            <div style="margin-top: 5px; cursor: pointer;">
+          <template #item="{ element }">
+            <div style="margin-top: 5px; cursor: pointer">
               <span>
                 <span>
-                  <Checkbox
-                    :id="'trace-finish-' + element.id" v-model="element.finished" class-name="terms" class="trace-checkbox"
-                    @input="updateTracefinished(element)"
-                  />
+                  <a-checkbox v-model:checked="element.finished" @change="updateTracefinished(element)" />
                 </span>
                 <span @click="openLine(element)">
                   <!-- eslint-disable-next-line -->
-                <span class="trace-summary" :class="{'font-color': element.timeLineCompletion==true}" v-html="element.summary" />
-                  <span class="glyphicon glyphicon-option-vertical trace-icon" aria-hidden="true" />
-                  <span class="label trace-user" :class="{'label-danger' : element.overdue == true, 'label-primary' : element.overdue != true}">
-                    <!-- eslint-disable-next-line -->
-                  <span v-html="element.receiveUserName" />&nbsp;
-                    <span>{{ formatDate(element.planFinishedDate) }}</span>
+                  <span class="trace-summary" :class="{
+                    'font-color': element.timeLineCompletion == true,
+                  }"
+                  >
+                    {{ element.summary }}
                   </span>
+                  <a-tag class="label" :color="element.overdue ? '#d9534f' : '#337ab7'">
+                    <span>{{ element.receiveUserName }}</span>&nbsp;
+                    <span>{{ formatDate(element.planFinishedDate) }}</span>
+                  </a-tag>
                 </span>
-                <a class="fa-pull-right" style="color: blue;" @click="edit(element)">编辑</a>
+                <a style="color: blue; font-size: 14px" @click="edit(element)">编辑</a>
               </span>
             </div>
           </template>
         </vuedraggable>
       </div>
     </div>
-
-    <div>
-      <div class="pull-left">
-        <span>第{{ (pagination.current_page-1)*pagination.per_page+1 }}-{{ pagination.current_page*pagination.per_page }}条,共计{{ pagination.total }}条,每页显示</span>
-        <PageSizeSelect :size="pagination.per_page" @page-size-changed="gridSizeSelect" />
-        <span>条</span>
-      </div>
-
-      <div class="pull-right">
-        <VueBootstrapPagination :pagination="pagination" :callback="getDatas" @click="updateRouter" />
-      </div>
+    <div v-show="traceDtos && traceDtos.length" style="margin: 16px 0">
+      <AntdPagination ref="paginationRef" :pagination="pagination" @get-page-params="getPageParams" />
     </div>
-
-    <h3>&nbsp;</h3>
   </div>
+  <Loading v-if="loading" />
 </template>
 
 <script>
@@ -75,34 +64,28 @@ import Common from '../common/Common.js';
 import TraceCommon from './TraceCommon.js';
 import QueryWidget from '../widget/QueryWidget.vue';
 import TraceHeader from './TraceHeader.vue';
-;
-
-
 import TraceResource from './TraceResource.js';
 import vuedraggable from 'vuedraggable';
 
 export default {
   components: {
     QueryWidget,
-    
-    
     TraceHeader,
     vuedraggable,
-    
   },
-  data: function() {
+  data: function () {
     return {
-      'param': {
-        'condition': '',
-        'traceStatus': 'RUNNING',
-        'traceUserStatus': 'I_ATTEND_PROJECT_ALL',
-        'range': {
+      param: {
+        condition: '',
+        traceStatus: 'RUNNING',
+        traceUserStatus: 'I_ATTEND_PROJECT_ALL',
+        range: {
           start: 0,
           length: 20,
         },
       },
-      'totalSize': 1,
-      'traceDtos': [],
+      totalSize: 1,
+      traceDtos: [],
       pagination: {
         total: 0,
         per_page: 20, // required
@@ -110,46 +93,49 @@ export default {
         last_page: 10, // required
       },
       formatDate: TraceCommon.formatDate,
+      loading: false,
     };
   },
 
   watch: {
-    'param.traceStatus': function(val) {
+    'param.traceStatus': function (val) {
       console.log(this.$route);
       this.getDatas();
     },
-    'param.traceUserStatus': function(val,oldVal) {
+    'param.traceUserStatus': function (val, oldVal) {
       this.$router.push({
-        path: '/trace/traceList/'+ val,
+        path: '/trace/traceList/' + val,
         query: {
           currentPage: this.pagination.current_page,
           pageSize: this.pagination.per_page,
         },
       });
-      // this.$router.push('/trace/traceList/' + val);
       this.$route.params.traceState = val;
-      console.log(this.$route);
-      console.log(oldVal);
       this.param.traceUserStatus = this.$route.params.traceState;
-      // this.updateRouter();
-      // this.pagination.total = 0;
-      // this.pagination.per_page = Common.pageSize;
-      // this.pagination.current_page = 1;
-      // this.pagination.last_page = 10;
-      // this.pagination.from = 1;
-      // this.pagination.to = 10;
       this.getDatas();
     },
 
+    // 监听路由是否变化
+    $route(to, from) {
+      if (
+        to.query.currentPage != from.query.currentPage ||
+        to.query.pageSize != from.query.pageSize
+      ) {
+        this.pagination.current_page = Number(to.query.currentPage);
+        this.pagination.per_page = Number(to.query.pageSize);
+        this.getDatas();
+      }
+    },
+
     beforeRouteUpdate(to, from, next) {
       const newId = to.query.tempId;
       const oldId = from.query.tempId;
-      console.log(newId,oldId);    //345,123
+      console.log(newId, oldId); //345,123
       next();
     },
   },
 
-  mounted: function() {
+  mounted: function () {
     TraceResource.getImg();
     console.log(this.$route);
     this.param.traceUserStatus = this.$route.params.traceState;
@@ -158,11 +144,11 @@ export default {
     this.getDatas();
   },
   methods: {
-    gridSizeSelect: function(newPageSize) {
-      this.pagination.per_page = newPageSize;
-      this.pagination.current_page = 1;
-      // 刷新界面
-      this.updateRouter();
+    // 页数改变
+    getPageParams: function (page, pageSize) {
+      this.pagination.current_page = page;
+      this.pagination.per_page = pageSize;
+      this.updateRouter(true);
     },
     end(evt) {
       var _self = this;
@@ -174,163 +160,201 @@ export default {
         start: (_self.pagination.current_page - 1) * _self.pagination.per_page,
         length: _self.pagination.per_page,
       };
+      _self.loading = true;
       $.ajax({
         url: Common.getApiURL('TraceResource/sortTraceByTraceQueryParamDto'),
         type: 'post',
         dataType: 'json',
         contentType: 'application/json',
         data: JSON.stringify(_self.param),
-        beforeSend: function(request) {
+        beforeSend: function (request) {
           Common.addTokenToRequest(request);
         },
-        success: function(data) {
+        success: function (data) {
           _self.getDatas();
+          _self.loading = false;
         },
-        error: function(XMLHttpRequest, textStatus, errorThrown) {
+        error: function (XMLHttpRequest, textStatus, errorThrown) {
+          _self.loading = false;
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
     },
 
-    updateRouter: function () {
+    updateRouter: function (isPage) {
       // var uuid = Uuid.createUUID();
       console.log(this.param.traceUserStatus);
       this.$router.push({
-        path: '/trace/traceList/'+ this.param.traceUserStatus,
+        path: '/trace/traceList/' + this.param.traceUserStatus,
         query: {
           currentPage: this.pagination.current_page,
           pageSize: this.pagination.per_page,
         },
       });
-      console.log(this.$route);
-      // console.log(this.$router.query.currentPage);
-      // console.log(this.$router.query.pageSize);
-      // this.getDatas();
+      if (isPage) {
+        this.getDatas();
+      }
+    },
+
+    query: function () {
+      this.$refs.paginationRef.backFirstPage();
     },
     /**
-			 * 查询数据
-			 * @author GuoZhiBo 20180226
-			 */
-    getDatas: function() {
+     * 查询数据
+     * @author GuoZhiBo 20180226
+     */
+    getDatas: function () {
       var _self = this;
-      _self.param.condition = _self.$refs.queryWidget.getSearchText();
       _self.param.range = {
         start: (_self.pagination.current_page - 1) * _self.pagination.per_page,
         length: _self.pagination.per_page,
       };
+      _self.param.condition = '';
+      const val = _self.$refs.queryWidget.getSearchText();
+      if (val != undefined && val.length > 0) {
+        _self.param.condition = val;
+      }
+      _self.loading = true;
       $.ajax({
         url: Common.getApiURL('TraceResource/listByTraceQueryParamDto'),
         type: 'post',
         dataType: 'json',
         contentType: 'application/json',
         data: JSON.stringify(_self.param),
-        beforeSend: function(request) {
+        beforeSend: function (request) {
           Common.addTokenToRequest(request);
         },
-        success: function(data) {
+        success: function (data) {
           console.log(data);
           if (data != null && data.dataList != null) {
             _self.traceDtos = data.dataList;
           }
           _self.pagination.total = data.totalSize;
-          _self.pagination.last_page = Math.ceil(data.totalSize / data.range.length);
+          _self.pagination.last_page = Math.ceil(
+            data.totalSize / data.range.length,
+          );
+          _self.loading = false;
         },
-        error: function(XMLHttpRequest, textStatus, errorThrown) {
+        error: function (XMLHttpRequest, textStatus, errorThrown) {
+          _self.loading = false;
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
         },
       });
     },
 
-
-
-
     /**
-			 * 打开明细
-			 * @author GuoZhiBo 20171201
-			 */
-    openLine: function(obj) {
+     * 打开明细
+     * @author GuoZhiBo 20171201
+     */
+    openLine: function (obj) {
       this.$router.push({
         path: '/trace/trace/' + obj.id,
       });
     },
 
-
     /**
-			 * 编辑
-			 */
-    edit: function(item) {
+     * 编辑
+     */
+    edit: function (item) {
       var _self = this;
       this.$router.push('/trace/traceUpdate/' + item.id);
     },
 
     /**
-			 * 修改追踪单的状态
-			 * @author YangZhiJie 20171201
-			 */
-    updateTracefinished: function(trace) {
+     * 修改追踪单的状态
+     * @author YangZhiJie 20171201
+     */
+    updateTracefinished: function (trace) {
       var _self = this;
-      TraceResource.updateTraceFinished(trace.id).then(successData => {
-        _self.getDatas();
-
-      }, errorData => {
-        Common.processException(errorData);
-      });
+      TraceResource.updateTraceFinished(trace.id).then(
+        successData => {
+          _self.getDatas();
+        },
+        errorData => {
+          Common.processException(errorData);
+        },
+      );
     },
-
-
   },
 };
 </script>
 
 <style scoped>
-	.active-div {
-		background-color: #007aff;
-		color: white !important;
-	}
-
-	.div-btn {
-		float: left;
-		height: 40px;
-		text-align: center;
-		line-height: 40px;
-		color: #007aff;
-	}
-
-	.mid-div {
-		border-left: 1px solid #007aff;
-		border-right: 1px solid #007aff;
-	}
-
-	.font-color {
-		color: red;
-	}
-
-	.divrow {
-		border: 1px solid #007aff;
-		height: 40px;
-	}
-
-	.select select {
-		width: 100%;
-		height: 30px;
-	}
-
-	.trace-summary {
-		font-size: large;
-		margin-left: 5px;
-		margin-right: 5px;
-	}
-
-	.trace-icon {
-		opacity: 0.5;
-	}
-
-	.trace-user {
-		font-size: 100%;
-	}
-
-	.trace-checkbox {
-		display: inline;
-		font-size: large;
-	}
+.active-div {
+  background-color: #007aff;
+  color: white !important;
+}
+
+.div-btn {
+  float: left;
+  height: 40px;
+  text-align: center;
+  line-height: 40px;
+  color: #007aff;
+}
+
+.mid-div {
+  border-left: 1px solid #007aff;
+  border-right: 1px solid #007aff;
+}
+
+.form-group {
+  margin-top: 10px;
+}
+
+.list-box {
+  margin-top: 10px;
+  border: 1px solid #ddd;
+  padding: 10px 16px;
+  border-radius: 6px;
+}
+
+.font-color {
+  color: red;
+}
+
+.divrow {
+  border: 1px solid #007aff;
+  height: 40px;
+}
+
+.select select {
+  width: 100%;
+  height: 30px;
+}
+
+.trace-summary {
+  font-size: large;
+  margin-left: 5px;
+  margin-right: 5px;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  line-height: 1.42857143;
+  color: #333;
+}
+
+.trace-icon {
+  opacity: 0.5;
+}
+
+.trace-user {
+  font-size: 100%;
+}
+
+.trace-checkbox {
+  display: inline;
+  font-size: large;
+}
+
+.label {
+  display: inline;
+  padding: 0.2em 0.6em 0.3em;
+  font-size: 75%;
+  font-weight: 700;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  border-radius: 0.25em;
+}
 </style>

+ 43 - 45
src/trace/TraceLog.vue

@@ -5,87 +5,79 @@
 -->
 <template>
   <div>
-    <div class="panel panel-default">
-      <div class="panel-heading dashboard-header" @click.self="collapse()">
-        &nbsp;&nbsp;动态信息
-        <div class="pull-left">
-          <span
-            class="glyphicon"
-            :class="{
-              'glyphicon-triangle-bottom': !isCollapse,
-              'glyphicon glyphicon-triangle-top': isCollapse,
-            }"
-          />
-        </div>
-      </div>
-      <div v-if="isCollapse == true" id="trace-content" class="container-fluid">
-        <div v-for="item in traceLogs" :key="item.id">
-          <h5>
-            {{ item.dateTime }} &nbsp;{{ item.operatorUserName }}
-            <!-- eslint-disable-next-line -->
-            <div v-html="item.content" />
-          </h5>
-        </div>
-      </div>
-    </div>
+    <a-collapse v-model:activeKey="activeKey" @change="handleCollapseChange">
+      <a-collapse-panel key="1" header="动态信息">
+        <a-list v-if="traceLogs.length > 0">
+          <a-list-item v-for="item in traceLogs" :key="item.id">
+            <a-space direction="vertical" style="width: 100%">
+              <a-typography-text strong>
+                {{ item.dateTime }} &nbsp;{{ item.operatorUserName }}
+              </a-typography-text>
+              <!-- eslint-disable-next-line -->
+              <div id="content-editor-view-log" v-html="item.content" />
+            </a-space>
+          </a-list-item>
+        </a-list>
+        <a-alert v-else style="width: 100%;" message="暂无动态信息" type="warning" />
+      </a-collapse-panel>
+    </a-collapse>
   </div>
 </template>
 
 <script>
 import Common from '../common/Common.js';
+
 export default {
-  components: {
-    
-  },
-  // props: ['traceId'],
   props: {
     traceId: {
       type: String,
       default: null,
     },
+    className: {
+      type: String,
+      default: null,
+    },
+
   },
 
   data: function () {
     return {
       traceLogs: [],
-      isCollapse: false,
+      activeKey: [], // 默认不展开
     };
   },
 
   watch: {
-    traceId: function (curVal, oldVal) {
-      if (curVal != undefined && this.isCollapse == true) {
+    traceId: function (newVal) {
+      // 当traceId变化且面板已展开时,重新加载数据
+      if (newVal && this.activeKey.includes('1')) {
         this.getTraceLog();
       }
     },
   },
 
-  mounted: function () {
-    if (this.traceId != undefined && this.isCollapse == true) {
-      this.getTraceLog();
-    }
-  },
   methods: {
     /**
-     * 控制展开闭合
-     * @return {void}
+     * 处理折叠面板状态变化
      */
-    collapse: function () {
-      var _self = this;
-      _self.isCollapse = !_self.isCollapse;
-      _self.getTraceLog();
+    handleCollapseChange(keys) {
+      // 当面板被展开且traceLogs为空时加载数据
+      if (keys.includes('1') && this.traceLogs.length === 0) {
+        this.getTraceLog();
+      }
     },
+
     /**
      * 根据追踪单Id获取追踪日志信息
-     * @author GuoZhiBo 20171201
      */
     getTraceLog: function () {
       var _self = this;
+      if (!_self.traceId) return;
+
       $.ajax({
         url: Common.getApiURL('TraceLogResource/listByTraceId'),
         type: 'get',
         dataType: 'json',
-        async: false,
         data: {
           traceId: _self.traceId,
         },
@@ -94,6 +86,9 @@ export default {
         },
         success: function (data) {
           _self.traceLogs = data;
+          _self.$nextTick(() => {
+            Common.downloadByA('#content-editor-view-log');
+          });
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
           Common.processException(XMLHttpRequest, textStatus, errorThrown);
@@ -104,5 +99,8 @@ export default {
 };
 </script>
 
-<style>
-</style>
+<style scoped>
+.ant-list-item {
+  padding: 12px 0;
+}
+</style>

+ 89 - 118
src/trace/TraceName.vue

@@ -1,154 +1,97 @@
 <template>
   <div>
-    <Navbar
-      :title="'任务详细信息' + (trace == null ? '' : '-' + trace.projectName)"
-      :is-go-back="true"
-    />
+    <Navbar :title="'任务详细信息' + (trace == null ? '' : '-' + trace.projectName)" :is-go-back="true" />
     <div v-if="isflag">
-      <div>
-        <div class="row">
-          <div align="center">
-            <div v-if="finished" class="checked" @click="getTimeLineNumber()" />
-            <div v-else class="clickFinished" @click="getTimeLineNumber()">
-              <div
-                style="
-                  width: 50px;
-                  height: 50px;
-                  color: #9b9b9b;
-                  margin-top: 16px;
-                "
-              >
-                <span>点击完成任务</span>
-              </div>
+      <a-row justify="center" style="margin-bottom: 16px">
+        <a-col :span="24" style="text-align: center">
+          <div v-if="finished" class="checked" @click="getTimeLineNumber()" />
+          <div v-else class="clickFinished" @click="getTimeLineNumber()">
+            <div class="clickFinishedText">
+              <span>点击完成任务</span>
             </div>
           </div>
-        </div>
-      </div>
+        </a-col>
+      </a-row>
 
-      <div>
-        <div class="page-header">
-          <h3>
-            <!-- eslint-disable-next-line -->
-            <span v-html="trace.summary" />&nbsp;
-            <span
-              class="label"
-              :class="{
-                'label-danger': trace.overdue == true,
-                'label-primary': trace.overdue != true,
-              }"
-            >
-              责任人:{{ trace.receiveUserName }}, 时间节点:{{
-                formatDate(trace.planFinishedDate)
-              }}
-            </span>
-            <span class="pull-right">
-              &nbsp;
-              <button
-                type="button"
-                class="btn btn-link"
-                style="padding: 0px"
-                @click="copyShaneUrl"
-              >
-                复制链接
-              </button>
-            </span>
-            <span class="pull-right">
-              &nbsp;
-              <button
-                type="button"
-                class="btn btn-link"
-                style="padding: 0px"
-                @click="deleteTrace"
-              >
-                删除
-              </button>
-            </span>
-            <span class="pull-right">
-              <button
-                type="button"
-                class="btn btn-link"
-                style="padding: 0px"
-                @click="edit"
-              >
-                编辑
-              </button>
-            </span>
-          </h3>
-        </div>
-      </div>
+      <a-space direction="vertical" style="width: 100%">
+        <a-row align="middle">
+          <a-col :flex="1">
+            <a-typography-title :level="3" style="margin-bottom: 0;margin-top: 0px">
+              <span>{{ trace.summary }}</span>
+              <a-tag class="label" :color="trace.overdue ? '#d9534f' : '#337ab7'">
+                责任人:{{ trace.receiveUserName }}, 时间节点:{{
+                  formatDate(trace.planFinishedDate)
+                }}
+              </a-tag>
+            </a-typography-title>
+          </a-col>
+          <a-col>
+            <a-space>
+              <a-button style="padding: 4px" type="link" @click="edit">编辑</a-button>
+              <a-button style="padding: 4px" type="link" @click="deleteTrace">删除</a-button>
+              <a-button style="padding: 4px" type="link" @click="copyShaneUrl">复制链接</a-button>
+            </a-space>
+          </a-col>
+        </a-row>
+        <a-divider />
+
+        <!-- eslint-disable-next-line -->
+        <div id="content-editor-view" v-html="trace.detail" />
 
-      <!-- eslint-disable-next-line -->
-      <div id="v-html" v-html="trace.detail" />
-      <AudioField
-        :class-name="className"
-        :src-array="srcArray"
-        :is-readonly="'true'"
-        @delete-audio-src="undefined"
-      />
+        <AudioField :class-name="className" :src-array="srcArray" :is-readonly="'true'" @delete-audio-src="undefined" />
+      </a-space>
 
-      <TraceTimeLine
-        ref="traceTimeLine"
-        :trace-id="traceId"
-        :trace="trace"
-        @refresh-trace-log="refreshTraceLog"
-      />
+      <TraceTimeLine ref="traceTimeLine" :trace-id="traceId" :trace="trace" @refresh-trace-log="refreshTraceLog" />
 
-      <div class="page-header">
-        <h4>附件</h4>
+      <div style="margin-top: 20px">
+        <span>附件</span>
+        <a-divider />
       </div>
 
-      <TraceAttachment :trace-id="traceId">1</TraceAttachment>
+      <TraceAttachment :trace-id="traceId" :class-name="attachmentClassName" />
 
-      <div>
-        <div class="page-header">
-          <h4>评论信息</h4>
-        </div>
-        <TraceComment :trace-id="traceId" />
+      <div style="margin-top: 20px">
+        <span>评论信息</span>
+        <a-divider />
       </div>
 
-      <div>
-        <button
-          type="button"
-          style="width: 100%; margin-top: 5px"
-          class="btn btn-default"
-          aria-label="Left Align"
-          @click="openTraceComment"
-        >
-          <span class="glyphicon glyphicon-pencil" aria-hidden="true" />评论
-        </button>
-      </div>
+      <TraceComment :trace-id="traceId" />
+
+      <a-button block style="margin-top: 16px;background: #efefef" @click="openTraceComment">
+        <template #icon><edit-outlined /></template>
+        评论
+      </a-button>
 
       <div style="margin-top: 20px">
-        <TraceLog ref="traceLog" :trace-id="traceId" :trace="trace" />
+        <TraceLog ref="traceLog" :trace-id="traceId" :trace="trace" :class-name="className" />
       </div>
     </div>
     <Loading v-if="loading" />
   </div>
 </template>
+
 <script>
 import Common from '../common/Common.js';
-
 import UpladFile from '../widget/UpladFile.js';
 import TraceCommon from './TraceCommon.js';
 import TraceResource from './TraceResource.js';
-
 import TraceLog from './TraceLog.vue';
 import TraceComment from './TraceComment.vue';
 import TraceTimeLine from './TraceTimeLine.vue';
 import TraceAttachment from './TraceAttachment.vue';
-
 import AudioField from '../widget/AudioField.vue';
 import { Notify } from 'pc-component-v3';
+import { EditOutlined } from '@ant-design/icons-vue';
+
 export default {
   components: {
     TraceLog,
     TraceComment,
     TraceTimeLine,
-
     TraceAttachment,
-
     UpladFile,
     AudioField,
+    EditOutlined,
   },
   data: function () {
     this.formatDate = TraceCommon.formatDate;
@@ -160,6 +103,7 @@ export default {
       count: 0,
       attachmentList: [],
       className: 'com.leanwo.prodog.Trace',
+      attachmentClassName: 'com.leanwo.prodog.model.trace.TraceAttachment',
       srcArray: [],
       isflag: true,
       loading: false,
@@ -234,6 +178,9 @@ export default {
             if (data.audioSrcs != null) {
               _self.srcArray = JSON.parse(data.audioSrcs);
             }
+            _self.$nextTick(() => {
+              Common.downloadByA('#content-editor-view');
+            });
           }
         },
         error: function (XMLHttpRequest, textStatus, errorThrown) {
@@ -421,15 +368,22 @@ export default {
   },
 };
 </script>
+
 <style scoped>
-.mui-img {
-  width: 80px;
+.label {
+  display: inline;
+  padding: 0.2em 0.6em 0.3em;
+  font-size: 64%;
+  font-weight: 700;
+  line-height: 1;
+  color: #fff;
+  text-align: center;
+  white-space: nowrap;
+  vertical-align: baseline;
+  border-radius: 0.25em;
+  margin-left: 10px;
 }
 
-.mui-color {
-  background-color: #007aff;
-  color: white;
-}
 .checked {
   background: #69ba52;
   width: 80px;
@@ -437,7 +391,9 @@ export default {
   border-radius: 16px;
   position: relative;
   cursor: pointer;
+  margin: 0 auto;
 }
+
 .checked::after {
   content: "";
   background-color: white;
@@ -449,6 +405,7 @@ export default {
   border-radius: 8px;
   transform: rotate(34deg);
 }
+
 .checked::before {
   content: "";
   background-color: white;
@@ -460,6 +417,7 @@ export default {
   transform: rotate(-50deg);
   border-radius: 8px;
 }
+
 .clickFinished {
   border: 2px solid #9b9b9b;
   width: 80px;
@@ -468,5 +426,18 @@ export default {
   position: relative;
   cursor: pointer;
   user-select: none;
+  margin: 0 auto;
+}
+
+.clickFinishedText {
+  width: 50px;
+  height: 50px;
+  color: #9b9b9b;
+  margin-top: 16px;
+  margin-left: 15px;
+}
+
+:deep(.ant-divider-horizontal) {
+  margin: 10px 0 !important;
 }
-</style>
+</style>

+ 52 - 76
src/trace/TraceTimeLine.vue

@@ -5,82 +5,74 @@
 -->
 <template>
   <div>
-    <div>
-      <div class="page-header">
-        <h4>时间节点({{ number }}/{{ count }})({{ trace.projectTaskName }})</h4>
-      </div>
+    <div style="margin-top: 20px">
+      <span>时间节点({{ number }}/{{ count }})({{ trace.projectTaskName }})</span>
+      <a-divider />
     </div>
 
-    <div
+    <a-card
       v-if="timeLines != undefined && timeLines[0].id != undefined"
-      class="panel panel-default"
+      :bordered="true"
     >
-      <div class="panel-body">
-        <div
+      <a-list>
+        <a-list-item
           v-for="item in timeLines"
           :key="item.id"
-          class="after-selection--columns"
-          style="margin-top: 5px; cursor: pointer"
+          style="cursor: pointer; padding: 10px"
         >
-          <span>
-            <Checkbox
-              :id="'trace-finish-' + item.id"
-              v-model:value="item.finished"
-              class-name="terms"
-              class="trace-checkbox"
-              @update:value="openCloseTraceTimeLine(item)"
-            />
-          </span>
+          <a-row>
+            <a-col :span="24">
+              <a-checkbox
+                :id="'trace-finish-' + item.id"
+                v-model:checked="item.finished"
+                class="gap-style"
+                @change="() => openCloseTraceTimeLine(item)"
+              />
+              <span class="gap-style" @click="editTraceTimeLine(item)">
+                {{ item.content == undefined ? "请填写" : item.content }}</span>
+              <a-tag
+                class="gap-style"
+                :color="item.overdue == true ? '#d9534f' : '#337ab7'"
+                @click="editTraceTimeLine(item)"
+              >
+                {{ item.userName == undefined ? "" : item.userName }}
+                &nbsp;
+                {{ formatDate(item.endDate) }}
+              </a-tag>
+            </a-col>
+          </a-row>
+        </a-list-item>
+      </a-list>
+    </a-card>
 
-          <span class="big-icon trace-summary" @click="editTraceTimeLine(item)">
-            {{ item.content == undefined ? "请填写" : item.content }}
-          </span>
-
-          <span class="trace-summary" @click="editTraceTimeLine(item)">
-            <span
-              class="label"
-              :class="{
-                'label-danger': item.overdue == true,
-                'label-primary': item.overdue != true,
-              }"
-            >
-              {{ item.userName == undefined ? "" : item.userName }}
-              &nbsp;
-              {{ formatDate(item.endDate) }}
-            </span>
-          </span>
-        </div>
-      </div>
-    </div>
-
-    <button
+    <a-button
       v-if="trace.finished != true"
-      type="button"
-      style="width: 100%"
-      class="btn btn-default"
-      aria-label="Left Align"
+      type="default"
+      block
+      style="margin-top: 10px;background: #efefef"
       @click="createTraceTimeLine"
     >
-      <span class="glyphicon glyphicon-plus" aria-hidden="true" />添加时间节点
-    </button>
+      <template #icon><plus-outlined /></template>
+      添加时间节点
+    </a-button>
+
     <Loading v-if="loading" />
   </div>
 </template>
 
 <script>
-
 import Common from '../common/Common.js';
 import TraceCommon from './TraceCommon.js';
 import { Uuid } from 'pc-component-v3';
 import { Notify } from 'pc-component-v3';
 import MyProgress from '../widget/MyProgress.vue';
+import { PlusOutlined } from '@ant-design/icons-vue';
 
 export default {
   components: {
     MyProgress,
-    
+    PlusOutlined,
   },
-  // props: ['traceId', 'trace'],
   props: {
     traceId: {
       type: String,
@@ -88,7 +80,7 @@ export default {
     },
     trace: {
       type: Object,
-      default: function() {
+      default: function () {
         return null;
       },
     },
@@ -286,11 +278,10 @@ export default {
      */
     openCloseTraceTimeLine: function (traceTimeLine) {
       console.log(traceTimeLine);
-      // traceTimeLine.finished = !traceTimeLine.finished;
       var _self = this;
       console.log(_self.trace);
       if (_self.trace.finished != true) {
-        _self.loading=true;
+        _self.loading = true;
         $.ajax({
           url: Common.getApiURL(
             'TraceTimeLineResource/updateTraceTimeLineFinished',
@@ -302,12 +293,12 @@ export default {
             Common.addTokenToRequest(request);
           },
           success: function (data) {
-            _self.loading=false;
+            _self.loading = false;
             _self.refresh();
             _self.$emit('refreshTraceLog');
           },
           error: function (XMLHttpRequest, textStatus, errorThrown) {
-            _self.loading=false;
+            _self.loading = false;
             Common.processException(XMLHttpRequest, textStatus, errorThrown);
           },
         });
@@ -329,26 +320,11 @@ export default {
 </script>
 
 <style scoped>
-.mui-checkbox-self {
-  left: 0px !important;
-  top: 0px !important;
-}
-.font-color {
-  color: red;
+.gap-style {
+  margin-right: 10px;
+  font-size: 14px;
 }
-
-.big-icon {
-  font-size: large;
-}
-
-.trace-summary {
-  font-size: large;
-  margin-left: 5px;
-  margin-right: 5px;
-}
-
-.trace-checkbox {
-  display: inline;
-  font-size: large;
+:deep(.ant-card-body) {
+  padding: 8px !important;
 }
-</style>
+</style>

+ 44 - 60
src/trace/TraceTimeLineCreate.vue

@@ -8,57 +8,46 @@
   <div>
     <div>
       <Navbar title="时间节点" :is-go-back="true" />
-      <div>
-        <div class="form-group">
-          <label for="exampleInputEmail2">添加检查项</label>
-          <textarea
-            id="hcqk"
-            v-model="traceTimeLine.content"
-            style="width: 100%"
-            class="form-control"
-            rows="5"
-          />
-        </div>
-        <div class="form-group">
-          <label for="exampleInputEmail2">负责人</label>
-          <a-select
-            v-model:value="user"
-            style="width: 100%; height: 30px"
-            :options="allUsers"
-            @change="getUser"
-          />
-          <!-- <select
-            id="userName"
-            style="width: 100%; height: 30px"
-            class="form-control js-data-example-ajax"
-          /> -->
-        </div>
+      <a-space direction="vertical" style="width: 100%">
+        <a-form layout="vertical">
+          <a-form-item label="添加检查项">
+            <a-textarea
+              v-model:value="traceTimeLine.content"
+              :rows="5"
+              placeholder="请输入检查项内容"
+            />
+          </a-form-item>
 
-        <div class="form-group">
-          <label for="exampleInputEmail2">截止时间</label>
-          <a-date-picker
-            v-model:value="planFinishedDate"
-            style="width: 100%"
-            :show-today="false"
-            placeholder="请选择日期"
-            @change="getDate"
-          />
+          <a-form-item label="负责人">
+            <a-select
+              v-model:value="user"
+              style="width: 100%"
+              :options="allUsers"
+              placeholder="请选择负责人"
+              @change="getUser"
+            />
+          </a-form-item>
 
-          <!-- <Date v-model="traceTimeLine.endDate" style="width: 100%" /> -->
-        </div>
-        <div style="margin-top: 10px">
-          <button
-            type="text"
-            class="btn btn-default"
-            @click="saveTraceTimeLine()"
-          >
-            确认
-          </button>
-          <button type="text" class="btn btn-default" @click="returnBack()">
-            取消
-          </button>
-        </div>
-      </div>
+          <a-form-item label="截止时间">
+            <a-date-picker
+              v-model:value="planFinishedDate"
+              style="width: 100%"
+              :show-today="false"
+              placeholder="请选择日期"
+              @change="getDate"
+            />
+          </a-form-item>
+
+          <a-form-item>
+            <a-space>
+              <a-button type="primary" @click="saveTraceTimeLine()">
+                确认
+              </a-button>
+              <a-button @click="returnBack()"> 取消 </a-button>
+            </a-space>
+          </a-form-item>
+        </a-form>
+      </a-space>
     </div>
     <Loading v-if="loading" />
   </div>
@@ -81,16 +70,13 @@ export default {
         endDate: undefined,
       }, // 追踪单时间节点(输入参数)
       loading: false,
-      user:'',
+      user: '',
       allUsers: [],
       planFinishedDate: '',
     };
   },
 
   mounted: function () {
-    $('.input-tan').focus(function () {
-      document.activeElement.blur();
-    });
     this.initData();
   },
 
@@ -105,20 +91,19 @@ export default {
       this.loadSelectUserName();
     },
 
-    getDate:function (_,dateStr) {
-      const _self = this;  
+    getDate: function (_, dateStr) {
+      const _self = this;
       if (_self.planFinishedDate != undefined && _self.planFinishedDate != '') {
         _self.traceTimeLine.endDate = dateStr + ' 00:00:00';
       }
     },
-    /**@argument
+    /**
      * 保存当前时间节点数据
      */
     saveTraceTimeLine: function () {
       var _self = this;
       _self.traceTimeLine.traceId = _self.traceId;
       var content = _self.traceTimeLine.content;
-      // _self.traceTimeLine.endDate = _self.dateConvert(_self.planFinishedDate) + ' 00:00:00';
       var dates = _self.traceTimeLine.endDate;
       if (
         content != null &&
@@ -151,7 +136,7 @@ export default {
       }
     },
 
-    /**@argument
+    /**
      * 返回上一界面
      */
     returnBack: function () {
@@ -191,6 +176,7 @@ export default {
       this.traceTimeLine.userId = val.userId;
       this.traceTimeLine.userName = val.userName;
     },
+
     // 日期处理函数
     plusZero: function (n) {
       return n >= 10 ? n : '0' + n;
@@ -221,5 +207,3 @@ export default {
   },
 };
 </script>
-<style scoped>
-</style>

+ 56 - 59
src/trace/TraceTimeLineEdit.vue

@@ -15,54 +15,51 @@
   <div>
     <div>
       <Navbar title="时间节点" :is-go-back="true" />
-      <div>
-        <div class="form-group">
-          <label for="exampleInputEmail2">添加检查项</label>
-          <textarea
-            id="hcqk"
-            v-model="traceTimeLine.content"
-            style="width: 100%"
-            class="form-control"
-            rows="5"
-          />
-        </div>
-        <div class="form-group">
-          <label for="exampleInputEmail2">负责人</label>
-          <a-select
-            v-model:value="traceTimeLine.userName"
-            style="width: 100%; height: 30px"
-            :options="allUsers"
-            @change="getUser"
-          />
-        </div>
+      <a-space direction="vertical" style="width: 100%;">
+        <a-form layout="vertical">
+          <a-form-item label="添加检查项">
+            <a-textarea
+              v-model:value="traceTimeLine.content"
+              :rows="5"
+              placeholder="请输入检查项内容"
+            />
+          </a-form-item>
+          
+          <a-form-item label="负责人">
+            <a-select
+              v-model:value="traceTimeLine.userName"
+              style="width: 100%"
+              :options="allUsers"
+              placeholder="请选择负责人"
+              @change="getUser"
+            />
+          </a-form-item>
 
-        <div class="form-group">
-          <label for="exampleInputEmail2">截止时间</label>
-          <a-date-picker
-            v-model:value="planFinishedDate"
-            style="width: 100%"
-            :show-today="false"
-            placeholder="请选择日期"
-            @change="getDate"
-          />
-          <!-- <Date v-model="traceTimeLine.endDate" style="width: 100%" /> -->
-        </div>
-        <div style="margin-top: 10px">
-          <button
-            type="text"
-            class="btn btn-default"
-            @click="saveTraceTimeLine()"
-          >
-            确认
-          </button>
-          <button type="text" class="btn btn-default" @click="returnBack()">
-            取消
-          </button>
-          <button type="text" class="btn btn-default" @click="deleteById()">
-            删除
-          </button>
-        </div>
-      </div>
+          <a-form-item label="截止时间">
+            <a-date-picker
+              v-model:value="planFinishedDate"
+              style="width: 100%"
+              :show-today="false"
+              placeholder="请选择日期"
+              @change="getDate"
+            />
+          </a-form-item>
+
+          <a-form-item>
+            <a-space>
+              <a-button type="primary" @click="saveTraceTimeLine()">
+                确认
+              </a-button>
+              <a-button @click="returnBack()">
+                取消
+              </a-button>
+              <a-button danger @click="deleteById()">
+                删除
+              </a-button>
+            </a-space>
+          </a-form-item>
+        </a-form>
+      </a-space>
     </div>
     <Loading v-if="loading" />
   </div>
@@ -80,6 +77,7 @@ import localeData from 'dayjs/plugin/localeData';
 dayjs.extend(weekday);
 dayjs.extend(localeData);
 dayjs.locale('zh-cn');
+
 export default {
   components: {},
   data: function () {
@@ -101,9 +99,6 @@ export default {
   },
 
   mounted: function () {
-    $('.input-tan').focus(function () {
-      document.activeElement.blur();
-    });
     this.initData();
   },
 
@@ -117,11 +112,13 @@ export default {
       this.projectId = Number(this.$route.query.projectId);
       this.loadTraceTimeLine();
     },
+    
     // 获取负责人信息
     getUser: function (_, val) {
       this.traceTimeLine.userId = val.userId;
       this.traceTimeLine.userName = val.userName;
     },
+    
     /**
      * 加载时间节点Id
      */
@@ -151,20 +148,21 @@ export default {
         },
       });
     },
-    getDate:function (_,dateStr) {
+    
+    getDate: function (_, dateStr) {
       const _self = this;  
       if (_self.planFinishedDate != undefined && _self.planFinishedDate != '') {
-        _self.traceTimeLine.endDate  = dateStr + ' 00:00:00'; //完成时间
+        _self.traceTimeLine.endDate = dateStr + ' 00:00:00'; //完成时间
       }
     },
-    /**@argument
+    
+    /**
      * 保存当前时间节点数据
      */
     saveTraceTimeLine: function () {
       var _self = this;
       _self.traceTimeLine.traceId = _self.traceId;
       var content = _self.traceTimeLine.content;
-      // _self.traceTimeLine.endDate = _self.dateConvert(_self.planFinishedDate) + ' 00:00:00';
       var dates = _self.traceTimeLine.endDate;
       if (
         content != null &&
@@ -197,7 +195,7 @@ export default {
       }
     },
 
-    /**@argument
+    /**
      * 根据时间节点ID删除时间节点信息
      */
     deleteById: function () {
@@ -223,7 +221,7 @@ export default {
       });
     },
 
-    /**@argument
+    /**
      * 返回上一界面
      */
     returnBack: function () {
@@ -257,6 +255,7 @@ export default {
         },
       });
     },
+    
     // 日期处理函数
     plusZero: function (n) {
       return n >= 10 ? n : '0' + n;
@@ -286,6 +285,4 @@ export default {
     },
   },
 };
-</script>
-<style scoped>
-</style>
+</script>

+ 89 - 222
src/trace/TraceUpdate.vue

@@ -2,97 +2,86 @@
   <div>
     <div>
       <Navbar title="编辑任务" :is-go-back="true" />
-      <div v-if="traceDto != null">
-        <div class="form-group">
-          <label for="exampleInputEmail1">标题</label>
-          <input
-            id="exampleInputEmail1"
-            v-model="traceDto.summary"
-            autocomplete="off"
-            type="text"
-            class="form-control"
-            placeholder="标题"
+      <a-space direction="vertical" style="width: 100%">
+        <a-form layout="vertical">
+          <a-form-item label="标题">
+            <a-input
+              v-model:value="traceDto.summary"
+              autocomplete="off"
+              placeholder="标题"
+            />
+          </a-form-item>
+
+          <a-form-item label="内容">
+            <EditorWidget v-model="traceDto.detail" :class-name="className" />
+          </a-form-item>
+
+          <AudioField
+            :class-name="className"
+            :src-array="srcArray"
+            :is-readonly="'false'"
+            @handle-stop="handelEndRecord"
+            @delete-audio-src="deleteAudioSrc"
           />
-        </div>
-        <div class="form-group">
-          <label for="exampleInputPassword1">内容</label>
-          <div id="summernote" />
-        </div>
 
-        <div class="form-group">
-          <label for="projectName">项目</label>
-          <select
-            id="projectName"
-            v-model="traceDto.projectId"
-            class="form-control"
-            @change="changeProjectSelected()"
-          >
-            <option
-              v-for="project in projectItemUserListProject"
-              :key="project.id"
-              :value="project.projectItemId"
+          <a-form-item label="项目">
+            <a-select
+              v-model:value="traceDto.projectId"
+              placeholder="请选择项目"
+              style="width: 100%"
+              @change="changeProjectSelected()"
             >
-              {{ project.projectItemName }}
-            </option>
-          </select>
-        </div>
-        <AudioField
-          :class-name="className"
-          :src-array="srcArray"
-          :is-readonly="'false'"
-          @handle-stop="handelEndRecord"
-          @delete-audio-src="deleteAudioSrc"
-        />
-        <div class="form-group">
-          <label for="projectName">时间节点</label>
-          <ATreeselect
-            ref="treeSelect"
-            :multiple="false"
-            :options="projectTaskDtos"
-          />
-        </div>
-        <div class="form-group">
-          <label for="receiveUserId">责任人</label>
-          <select
-            id="receiveUserId"
-            v-model="traceDto.receiveUserId"
-            class="form-control"
-          >
-            <option value="" />
-            <option
-              v-for="user in projectItemUserListUser"
-              :key="user.userId"
-              :value="user.userId"
+              <a-select-option
+                v-for="project in projectItemUserListProject"
+                :key="project.id"
+                :value="project.projectItemId"
+              >
+                {{ project.projectItemName }}
+              </a-select-option>
+            </a-select>
+          </a-form-item>
+
+          <a-form-item label="责任人">
+            <a-select
+              v-model:value="traceDto.receiveUserId"
+              placeholder="请选择责任人"
+              style="width: 100%"
             >
-              {{ user.userName }}
-            </option>
-          </select>
-        </div>
+              <a-select-option value="" />
+              <a-select-option
+                v-for="user in projectItemUserListUser"
+                :key="user.userId"
+                :value="user.userId"
+              >
+                {{ user.userName }}
+              </a-select-option>
+            </a-select>
+          </a-form-item>
 
-        <div class="form-group">
-          <label>完成时间</label>
-          <a-date-picker
-            v-model:value="planFinishedDate"
-            style="width: 100%"
-            :show-today="false"
-            placeholder="请选择完成日期"
-            @change="getDate"
-          />
-          <!-- <Date
-            v-model="traceDto.planFinishedDate"
-            style="width: 100%"
-          /> -->
-        </div>
+          <a-form-item label="时间节点">
+            <ATreeselect
+              ref="treeSelect"
+              :multiple="false"
+              :options="projectTaskDtos"
+            />
+          </a-form-item>
+
+          <a-form-item label="完成时间">
+            <a-date-picker
+              v-model:value="planFinishedDate"
+              style="width: 100%"
+              placeholder="请选择完成日期"
+              @change="getDate"
+            />
+          </a-form-item>
 
-        <button
-          style="width: 100%; background-color: #007aff; color: white"
-          type="button"
-          class="btn btn-blue search-button"
-          @click="updateTrace()"
-        >
-          保存
-        </button>
-      </div>
+          <a-form-item>
+            <a-button type="primary" block @click="updateTrace()">
+              保存
+            </a-button>
+          </a-form-item>
+        </a-form>
+      </a-space>
     </div>
     <Loading v-if="loading" />
   </div>
@@ -100,29 +89,24 @@
 <script>
 import TraceResource from './TraceResource.js';
 import Common from '../common/Common.js';
-import UpladFile from '../widget/UpladFile.js';
-import TraceCommon from './TraceCommon.js';
-
 import { Notify } from 'pc-component-v3';
-
 import Treeselect from '../widget/TreeSelect.vue';
 import ATreeselect from '../widget/ATreeSelect.vue';
 import AudioField from '../widget/AudioField.vue';
-import { message } from 'ant-design-vue';
 import zhCN from 'ant-design-vue/es/locale/zh_CN';
 import dayjs from 'dayjs';
 import 'dayjs/locale/zh-cn';
 import weekday from 'dayjs/plugin/weekday';
 import localeData from 'dayjs/plugin/localeData';
- 
+import EditorWidget from '../widget/EditorWidget.vue';
+
 dayjs.extend(weekday);
 dayjs.extend(localeData);
 dayjs.locale('zh-cn');
 
 export default {
   components: {
-    UpladFile,
-
+    EditorWidget,
     Treeselect,
     ATreeselect,
     AudioField,
@@ -134,13 +118,13 @@ export default {
       className: 'com.leanwo.prodog.Trace',
       projectItemUserListUser: [],
       projectItemUserListProject: [],
-      summernoteInitSuccess: false, // summernote初始化成功
       projectTaskDtos: [], //项目任务时间点
       isflag: true,
       srcArray: [],
       loading: false,
       locale: zhCN,
       planFinishedDate: '',
+      content: '<p>222345</p>',
     };
   },
   watch: {
@@ -164,9 +148,6 @@ export default {
   },
 
   beforeUnmount: function () {
-    if (this.summernoteInitSuccess == true) {
-      $('#summernote').summernote('destroy');
-    }
     //界面销毁时,修改插入连接的display属性
     $('.note-link-popover').removeAttr('style');
   },
@@ -194,21 +175,15 @@ export default {
     back: function () {
       history.back();
     },
-    getDate:function (_,dateStr) {
-      const _self = this;  
+    getDate: function (_, dateStr) {
+      const _self = this;
       if (_self.planFinishedDate != undefined && _self.planFinishedDate != '') {
         _self.traceDto.planFinishedDate = dateStr + ' 00:00:00'; //完成时间
       }
     },
     updateTrace: function () {
       var _self = this;
-      _self.traceDto.detail = $('#summernote').summernote('code');
       _self.traceDto.projectTaskId = _self.$refs.treeSelect.getValue();
-      // if (_self.planFinishedDate != undefined && _self.planFinishedDate != '') {
-      //   _self.traceDto.planFinishedDate = _self.dateConvert(
-      //     _self.planFinishedDate,
-      //   ) + ' 00:00:00'; //完成时间
-      // }
       if (_self.traceDto.summary != '' && _self.traceDto.summary != undefined) {
         _self.loading = true;
         $.ajax({
@@ -234,73 +209,6 @@ export default {
       }
     },
 
-    //图片上传
-    sendFile: function (file, editor, $editable) {
-      var _self = this;
-      UpladFile.photoCompress(
-        file,
-        {
-          quality: 0.2,
-        },
-        function (base64Codes) {
-          var bl = UpladFile.convertBase64UrlToBlob(base64Codes);
-          var rst = new FormData();
-          rst.append('images', bl, 'file_' + Date.parse(new Date()) + '.jpg');
-          rst.append('className', _self.className);
-          $.ajax({
-            url: Common.getApiURL('file/imageUpload'),
-            type: 'post',
-            data: rst,
-            contentType: false,
-            processData: false,
-            beforeSend: function (request) {
-              Common.addTokenToRequest(request);
-            },
-            success: function (data) {
-              if (data.errorCode == 0) {
-                if (data.datas) {
-                  var imageName = data.datas[0];
-                  // 方法1: 直接插入图片,由于图片不能加载,导致导致插入失败。
-                  // $("#summernote").summernote('insertImage', Common.getResourceUrl("image", _self.className, imageName), 'image name');
-
-                  // 方法2:创建img节点,然后插入<img>节点,可以插入,但是图片没有显示,点击一下code,然后就显示了。
-                  // var node = document.createElement('img') // 创建节点,用于包裹视频,再加这一层是方便调视频样式
-                  // node.setAttribute("authSrc", Common.getResourceUrl("image", _self.className, imageName));
-                  // node.setAttribute("is", "auth-img");
-                  // $("#summernote").summernote('insertNode', node);
-
-                  // 方法3:重新给code赋值,此时丢失了鼠标焦点,此时不满足要求。
-                  // var get_code = $('#summernote').summernote('code');
-                  // $('#summernote').summernote('code', get_code);
-
-                  // 方法4:插入文本(尝试失败)
-                  //let imageText = "<img is='auth-img' authSrc='" + Common.getResourceUrl("image", _self.className, imageName) + "'/>";
-                  //$('#summernote').summernote('insertText', imageText);
-
-                  // 方法5: 插入div,div中嵌入img,测试OK
-                  //                          var node = document.createElement('div') // 创建节点,用于包裹视频,再加这一层是方便调视频样式
-                  //                          node.innerHTML = "<img is='auth-img' authSrc='" + Common.getResourceUrl("image", _self.className, imageName) + "' />";
-                  //                          $("#summernote").summernote('insertNode', node);
-                  $('#summernote').summernote(
-                    'insertImage',
-                    Common.getResourceUrl('image', _self.className, imageName),
-                    'image name',
-                  );
-                }
-              } else {
-                message.error(data.errorMessage);
-              }
-            },
-            error: function () {
-              Notify.error('提示', '上传失败!');
-              return;
-            },
-          });
-          return rst.file;
-        },
-      );
-    },
-
     /**
      * 初始化项目
      */
@@ -375,8 +283,11 @@ export default {
         TraceResource.uniqueByTraceId(_self.traceId).then(
           successData => {
             _self.traceDto = successData;
-            if(successData.planFinishedDate){
-              _self.planFinishedDate = dayjs(successData.planFinishedDate,'YYYY-MM-DD');
+            if (successData.planFinishedDate) {
+              _self.planFinishedDate = dayjs(
+                successData.planFinishedDate,
+                'YYYY-MM-DD',
+              );
             }
             if (
               successData != undefined &&
@@ -385,53 +296,6 @@ export default {
               _self.srcArray = JSON.parse(successData.audioSrcs);
             }
             _self.changeProjectSelected();
-            $('#summernote').summernote({
-              toolbar: [
-                [
-                  'insert',
-                  [
-                    'link',
-                    'picture',
-                    'video',
-                    'audio',
-                    'hr',
-                    'table', //插件
-                    'fontname',
-                    'fontsize',
-                    'color',
-                    'bold',
-                    'italic',
-                    'underline',
-                    'strikethrough',
-                    'clear', //字体样式
-                    'style',
-                    'ol',
-                    'ul',
-                    'paragraph',
-                    'height', //段落样式
-                    'fullscreen',
-                    'codeview',
-                    'undo',
-                    'redo',
-                    'help', //Misc
-                  ],
-                ],
-              ],
-              height: 400,
-              minHeight: 300,
-              maxHeight: 500,
-              focus: true,
-              lang: 'zh-CN',
-              callbacks: {
-                onImageUpload: function (files, editor, $editable) {
-                  _self.sendFile(files[0], editor, $editable);
-                },
-              },
-            });
-            if (_self.traceDto != null) {
-              $('#summernote').summernote('code', _self.traceDto.detail);
-            }
-            _self.summernoteInitSuccess = true;
           },
           errorData => {
             Common.processException(errorData);
@@ -516,4 +380,7 @@ export default {
 </script>
 
 <style scoped>
-</style>
+:deep(.ant-form-item){
+  margin-bottom: 8px !important;
+}
+</style>

+ 100 - 94
src/trace/UseFinishedTrace.vue

@@ -1,69 +1,54 @@
 <template>
   <div>
-    <Navbar
-      :title="&quot;团队-&quot; + userName + &quot; 已完成的任务&quot; + &quot;(&quot; + pagination.total + &quot;)&quot;"
-      :is-go-back="true"
-    />
+    <Navbar :title="'团队-' + userName + ' 已完成的任务' + '(' + pagination.total + ')'" :is-go-back="true" />
 
-    <p
-      v-if="traces == null || traces.length == 0"
-      class="bg-danger"
-      style="padding: 15px;"
-    >
-      未完成任何任务
-    </p>
+    <a-space direction="vertical" style="width: 100%">
+      <a-input v-model:value="content" placeholder="任务名称、任务内容" allow-clear @press-enter="query" />
 
-    <div
-      v-else
-      class="panel panel-default"
-      style="margin-bottom: 10px;"
-    >
-      <div class="panel-body">
-        <input v-model="content" autocomplete="off" type="text" class="form-control" placeholder="任务名称、任务内容" @blur="initData" @keyup.enter="initData" />
-        <div
-          v-for="trace in traces"
-          :key="trace.id"
-          style="margin-top: 5px; cursor: pointer;"
-        >
-          <span>
-            <span>
-              <Checkbox
-                :id="'trace-finish-' + trace.id"
-                v-model="trace.finished"
-                class-name="terms"
-                class="trace-checkbox"
-                @input="updateTracefinished(trace)"
-              />
-            </span>
-            <span @click="openLine(trace)">
-              <!-- eslint-disable-next-line -->
-              <span v-html="trace.summary" class="trace-summary" :class="{'font-color': trace.timeLineCompletion==true}" />
-              <span
-                class="glyphicon glyphicon-option-vertical trace-icon"
-                aria-hidden="true"
-              />
-              <span class="label label-primary trace-user">
-                <!-- eslint-disable-next-line -->
-                <span v-html="trace.receiveUserName" />
-                <span>{{ formatDate(trace.planFinishedDate) }}</span>
-              </span>
-              <span class="badge">
-                <span>{{ trace.projectName }}</span>
-              </span>
-              <span class="badge">
-                <span>完成时间:{{ formatDate(trace.finishedDate) }}</span>
-              </span>
-            </span>
-          </span>
-        </div>
+      <a-alert v-if="traces == null || traces.length == 0" message="未安排任何任务" type="warning" />
+
+      <div v-else>
+        <a-list :data-source="traces" size="small" :split="false">
+          <template #renderItem="{ item: trace }">
+            <a-list-item style="padding: 4px 0">
+              <a-row>
+                <a-col :span="24">
+                  <a-checkbox
+                    :id="'trace-finish-' + trace.id" v-model:checked="trace.finished" class="gap-style"
+                    @change="() => updateTracefinished(trace)"
+                  />
+                  <span style="cursor: pointer" @click="openLine(trace)">
+                    <!-- eslint-disable-next-line -->
+                    <span class="trace-summary" v-html="trace.summary" />
+                    <a-tag class="gap-style" :color="trace.overdue == true ? '#d9534f' : '#337ab7'">
+                      <!-- eslint-disable-next-line -->
+                      <span class="gap-style" v-html="trace.receiveUserName" />
+                      <span>
+                        {{ formatDate(trace.planFinishedDate) }}
+                      </span>
+                    </a-tag>
+                    <a-tag color="#777">
+                      <span>
+                        {{ trace.projectName }}
+                      </span>
+                    </a-tag>
+                    <a-tag color="#777">
+                      <span>
+                        完成时间:{{ formatDate(trace.finishedDate) }}
+                      </span>
+                    </a-tag>
+                  </span>
+                </a-col>
+              </a-row>
+            </a-list-item>
+          </template>
+        </a-list>
+      </div>
+
+      <div v-show="traces && traces.length" style="margin: 16px 0">
+        <AntdPagination ref="paginationRef" :pagination="pagination" @get-page-params="getPageParams" />
       </div>
-    </div>
-    <VueBootstrapPagination
-      v-if="traces.length"
-      :pagination="pagination"
-                    
-      :callback="initData"
-    />
+    </a-space>
   </div>
 </template>
 
@@ -76,7 +61,8 @@ import { Uuid } from 'pc-component-v3';
 
 export default {
   components: {
-    TraceResource, TraceCommon, 
+    TraceResource,
+    TraceCommon,
   },
   data: function () {
     return {
@@ -88,9 +74,9 @@ export default {
       formatDate: TraceCommon.formatDate,
       pagination: {
         total: 0,
-        per_page: 50,    // 每页10条信息
+        per_page: 50, // 每页10条信息
         current_page: 1, // 当前页码
-        last_page: 10,    // 最后页码
+        last_page: 10, // 最后页码
       },
       content: undefined,
     };
@@ -99,10 +85,17 @@ export default {
     this.initData();
   },
   methods: {
-
+    query: function () {
+      this.$refs.paginationRef.backFirstPage();
+    },
+    getPageParams: function (page, pageSize) {
+      this.pagination.current_page = page;
+      this.pagination.per_page = pageSize;
+      this.initData();
+    },
     /**
-         * 初始化人员
-         */
+     * 初始化人员
+     */
     initData: function () {
       var _self = this;
       _self.traces.splice(0, _self.traces.length);
@@ -114,64 +107,77 @@ export default {
         dataType: 'json',
         contentType: 'application/json',
         data: {
-          'receiveUserId': _self.userId,
-          'currentPage': _self.pagination.current_page,
-          'pageSize': _self.pagination.per_page,
-          'content': _self.content, 
+          receiveUserId: _self.userId,
+          currentPage: _self.pagination.current_page,
+          pageSize: _self.pagination.per_page,
+          content: _self.content,
         },
         beforeSend: function (request) {
           Common.addTokenToRequest(request);
         },
         success: function (data) {
           _self.pagination.total = data.totalSize;
-          _self.pagination.last_page = Math.ceil(_self.pagination.total / _self.pagination.per_page);
           _self.traces = data.traceDtos;
+          _self.$nextTick(() => {
+            Common.downloadByA();
+          });
         },
       });
     },
 
     /**
-         * 打开明细
-         * @author ZhangTeng 2019131
-         */
+     * 打开明细
+     * @author ZhangTeng 2019131
+     */
     openLine: function (obj) {
-      this.$router.push({ 
-        path: '/trace/trace/' + obj.id, 
+      this.$router.push({
+        path: '/trace/trace/' + obj.id,
       });
     },
     /**
-    * 修改追踪单的状态
-    * @author YangZhiJie 20171201
-    */
+     * 修改追踪单的状态
+     * @author YangZhiJie 20171201
+     */
     updateTracefinished: function (trace) {
       var _self = this;
-      TraceResource.updateTraceFinished(trace.id).then(successData => {
-        _self.initData();
-      }, errorData => {
-        Common.processException(errorData);
-      });
+      TraceResource.updateTraceFinished(trace.id).then(
+        successData => {
+          _self.initData();
+        },
+        errorData => {
+          Common.processException(errorData);
+        },
+      );
     },
-
   },
 };
 </script>
 
 <style scoped>
 .trace-summary {
-    font-size: large;
-    margin-left: 5px;
-    margin-right: 5px;
+  font-size: large;
+  margin-left: 5px;
+  margin-right: 5px;
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  line-height: 1.42857143;
+  color: #333;
+  margin-right: 10px;
 }
 
 .trace-icon {
-    opacity: 0.5;
+  opacity: 0.5;
 }
 
 .trace-user {
-    font-size: 100%;
+  font-size: 100%;
 }
+
 .trace-checkbox {
-    display: inline;
-    font-size: large;
+  display: inline;
+  font-size: large;
+}
+
+.gap-style {
+  margin-right: 10px;
 }
-</style>
+</style>

+ 141 - 142
src/trace/UserNotFinishedTrace.vue

@@ -1,81 +1,50 @@
 <template>
   <div>
-    <Navbar
-      :title="&quot;团队-&quot; + userName + &quot; 待完成任务&quot; + &quot;(&quot; + pagination.total + &quot;)&quot;"
-      :is-go-back="true"
-    />
-
-    <div>
-      <button
-        type="button"
-        class="btn btn-link"
-        @click="finished"
-      >
-        查看已完成的任务
-      </button>
-    </div>
-
-    <p
-      v-if="traces == null || traces.length == 0"
-      class="bg-danger"
-      style="padding: 15px;"
-    >
-      未安排任何任务
-    </p>
-
-    <div
-      v-else
-      class="panel panel-default"
-      style="margin-bottom: 10px;"
-    >
-      <div class="panel-body">
-        <input v-model="content" autocomplete="off" type="text" class="form-control" placeholder="任务名称、任务内容" @blur="initData" @keyup.enter="initData" />
-        <div
-          v-for="trace in traces"
-          :key="trace.id"
-          style="margin-top: 5px; cursor: pointer;"
-        >
-          <span>
-            <span>
-              <Checkbox
-                :id="'trace-finish-' + trace.id"
-                v-model="trace.finished"
-                class-name="terms"
-                class="trace-checkbox"
-                style=""
-                @input="updateTracefinished(trace)"
-              />
-            </span>
-            <span @click="openLine(trace)">
-              <!-- eslint-disable-next-line -->
-              <span v-html="trace.summary" class="trace-summary" :class="{'font-color': trace.timeLineCompletion==true}" />
-              <span
-                class="glyphicon glyphicon-option-vertical trace-icon"
-                aria-hidden="true"
-              />
-              <span
-                class="label trace-user"
-                :class="{'label-danger' : trace.overdue == true, 'label-primary' : trace.overdue != true}"
-              >
-                <!-- eslint-disable-next-line -->
-                <span v-html="trace.receiveUserName" />
-                <span>{{ formatDate(trace.planFinishedDate) }}</span>
-              </span>
-              <span class="badge">
-                <span>{{ trace.projectName }}</span>
-              </span>
-            </span>
-          </span>
-        </div>
+    <Navbar :title="'团队-' + userName + ' 待完成任务' + '(' + pagination.total + ')'" :is-go-back="true" />
+
+    <a-space direction="vertical" style="width: 100%">
+      <a-input v-model:value="content" placeholder="任务名称、任务内容" allow-clear @press-enter="query" />
+      <a-alert v-if="traces == null || traces.length == 0" message="未安排任何任务" type="warning" />
+      <div v-else>
+        <a-list :data-source="traces" size="small" :split="false">
+          <template #renderItem="{ item: trace }">
+            <a-list-item style="padding: 4px 0">
+              <a-row>
+                <a-col :span="24">
+                  <a-checkbox
+                    :id="'trace-finish-' + trace.id" v-model:checked="trace.finished" class="gap-style"
+                    @change="() => updateTracefinished(trace)"
+                  />
+                  <span style="cursor: pointer" @click="openLine(trace)">
+                    <!-- eslint-disable-next-line -->
+                    <span class="trace-summary" v-html="trace.summary" />
+                    <a-tag class="gap-style" :color="trace.overdue == true ? '#d9534f' : '#337ab7'">
+                      <!-- eslint-disable-next-line -->
+                      <span class="gap-style" v-html="trace.receiveUserName" />
+                      <span>
+                        {{ formatDate(trace.planFinishedDate) }}
+                      </span>
+                    </a-tag>
+                    <a-tag color="#777">
+                      <span>{{ trace.projectName }}</span>
+                    </a-tag>
+                  </span>
+                </a-col>
+              </a-row>
+            </a-list-item>
+          </template>
+        </a-list>
+      </div>
+
+      <div v-show="traces && traces.length" style="margin: 16px 0">
+        <AntdPagination ref="paginationRef" :pagination="pagination" @get-page-params="getPageParams" />
+      </div>
+      <div>
+        <a-button type="link" style="color: green; padding: 0px" @click="finished">
+          查看已完成的任务
+        </a-button>
       </div>
-    </div>
-
-    <VueBootstrapPagination
-      v-if="traces.length"
-      :pagination="pagination"
-                    
-      :callback="initData"
-    />
+    </a-space>
   </div>
 </template>
 
@@ -88,7 +57,8 @@ import { Uuid } from 'pc-component-v3';
 
 export default {
   components: {
-    TraceCommon, TraceResource,  
+    TraceCommon,
+    TraceResource,
   },
   data: function () {
     return {
@@ -99,33 +69,40 @@ export default {
       formatDate: TraceCommon.formatDate,
       pagination: {
         total: 0,
-        per_page: 50,    // 每页10条信息
+        per_page: 50, // 每页10条信息
         current_page: 1, // 当前页码
-        last_page: 10,    // 最后页码
+        last_page: 10, // 最后页码
       },
-      content:undefined,
+      content: undefined,
     };
   },
 
   mounted: function () {
     this.initData();
-
   },
   methods: {
-
     /**
-         * 打开明细
-         * @author ZhangTeng 2019131
-         */
+     * 打开明细
+     * @author ZhangTeng 2019131
+     */
     openLine: function (obj) {
-      this.$router.push({ 
-        path: '/trace/trace/' + obj.id, 
+      this.$router.push({
+        path: '/trace/trace/' + obj.id,
       });
     },
-        
+
+    query: function () {
+      this.$refs.paginationRef.backFirstPage();
+    },
+    getPageParams: function (page, pageSize) {
+      this.pagination.current_page = page;
+      this.pagination.per_page = pageSize;
+      this.initData();
+    },
+
     /**
-         * 初始化人员
-         */
+     * 初始化人员
+     */
     initData: function () {
       var _self = this;
       _self.traces.splice(0, _self.traces.length);
@@ -137,32 +114,40 @@ export default {
         dataType: 'json',
         contentType: 'application/json',
         data: {
-          'receiveUserId': _self.userId,
-          'currentPage': _self.pagination.current_page,
-          'pageSize': _self.pagination.per_page,
-          'content': _self.content,
+          receiveUserId: _self.userId,
+          currentPage: _self.pagination.current_page,
+          pageSize: _self.pagination.per_page,
+          content: _self.content,
         },
         beforeSend: function (request) {
           Common.addTokenToRequest(request);
         },
         success: function (data) {
           _self.pagination.total = data.totalSize;
-          _self.pagination.last_page = Math.ceil(_self.pagination.total / _self.pagination.per_page);
+          _self.pagination.last_page = Math.ceil(
+            _self.pagination.total / _self.pagination.per_page,
+          );
           _self.traces = data.traceDtos;
+          _self.$nextTick(() => {
+            Common.downloadByA();
+          });
         },
       });
     },
     /**
-		* 修改追踪单的状态
-		* @author YangZhiJie 20171201
-		*/
+     * 修改追踪单的状态
+     * @author YangZhiJie 20171201
+     */
     updateTracefinished: function (trace) {
       var _self = this;
-      TraceResource.updateTraceFinished(trace.id).then(successData => {
-        _self.initData();
-      }, errorData => {
-        Common.processException(errorData);
-      });
+      TraceResource.updateTraceFinished(trace.id).then(
+        successData => {
+          _self.initData();
+        },
+        errorData => {
+          Common.processException(errorData);
+        },
+      );
     },
 
     finished: function () {
@@ -180,70 +165,84 @@ export default {
 
 <style scoped>
 .boxes .box {
-    display: block;
-    width: 90px;
-    height: 235px;
-    border-radius: 4px;
-    text-align: center;
-    color: #000;
+  display: block;
+  width: 90px;
+  height: 235px;
+  border-radius: 4px;
+  text-align: center;
+  color: #000;
 }
+
 .boxes .box__statistics {
-    display: block;
-    width: 90px;
-    height: 90px;
-    padding: 10px;
-    margin: 15px auto 0;
-    border-radius: 50%;
-    color: #fff;
-    font-size: 46px;
+  display: block;
+  width: 90px;
+  height: 90px;
+  padding: 10px;
+  margin: 15px auto 0;
+  border-radius: 50%;
+  color: #fff;
+  font-size: 46px;
 }
+
 .boxes .box__statistics--upcoming2 {
-    background-color: #f6aa39;
+  background-color: #f6aa39;
 }
+
 a {
-    text-decoration: none;
-    margin: 10px;
-    font-size: 100%;
-    vertical-align: baseline;
-    background: transparent;
+  text-decoration: none;
+  margin: 10px;
+  font-size: 100%;
+  vertical-align: baseline;
+  background: transparent;
 }
+
 .boxes .box__title {
-    margin-top: 25px;
-    font-size: 24px;
+  margin-top: 25px;
+  font-size: 24px;
 }
+
 .boxes .box__title__weeks {
-    font-size: 18px;
+  font-size: 18px;
 }
+
 .box:hover {
-    background-color: ghostwhite;
-    width: 90px;
-    height: 235px;
+  background-color: ghostwhite;
+  width: 90px;
+  height: 235px;
 }
+
 .row {
-    height: 200px;
-    margin-bottom: 10px;
+  height: 200px;
+  margin-bottom: 10px;
 }
+
 .dashboard-header {
-    cursor: pointer;
-    font-family: "\5FAE\8F6F\96C5\9ED1";
-    font-weight: bold;
-    font-size: 1.1em;
+  cursor: pointer;
+  font-family: "\5FAE\8F6F\96C5\9ED1";
+  font-weight: bold;
+  font-size: 1.1em;
 }
+
 .trace-summary {
-    font-size: large;
-    margin-left: 5px;
-    margin-right: 5px;
+  font-size: large;
+  margin-left: 5px;
+  margin-right: 5px;
 }
 
 .trace-icon {
-    opacity: 0.5;
+  opacity: 0.5;
 }
 
 .trace-user {
-    font-size: 100%;
+  font-size: 100%;
 }
+
 .trace-checkbox {
-    display: inline;
-    font-size: large;
+  display: inline;
+  font-size: large;
+}
+
+.gap-style {
+  margin-right: 10px;
 }
-</style>
+</style>

+ 13 - 11
src/widget/AudioField.vue

@@ -6,14 +6,13 @@
 <template>
   <div>
     <div v-if="isReadonly == 'false'" style="margin-bottom: 10px">
-      <button
-        type="button"
-        class="btn btn-info"
+      <a-button
+        style="background:#48b8d9;color:white;"
         @mousedown.prevent="mouseStart"
       >
         <span class="glyphicon glyphicon-volume-up" aria-hidden="true" />
         {{ tiptext }}
-      </button>
+      </a-button>
     </div>
     <template v-if="srcArray.length > 0">
       <div
@@ -22,15 +21,14 @@
         style="margin-bottom: 10px; display: inline-flex; align-items: center"
       >
         <AuthAudio :audio-src="getUrl(item)" :token="token" />
-        <button
+        <a-button
           v-if="isReadonly == 'false'"
-          type="button"
-          class="btn btn-link"
+          type="link"
           style="flex: 1"
           @click="deleteAudioSrc(index)"
         >
           <span class="glyphicon glyphicon-remove" aria-hidden="true" /> 删除
-        </button>
+        </a-button>
 
         <div class="clearfix" />
       </div>
@@ -42,6 +40,7 @@
 import Common from '../common/Common.js';
 import recording from './recorder.js';
 import AuthAudio from './AuthAudio.vue';
+import { message } from 'ant-design-vue';
 export default {
   name: 'MRecorder',
   components: {
@@ -64,7 +63,8 @@ export default {
       default: null,
     }, 
     isReadonly: {
-      type: Boolean,
+      type: String,
+      default:'false',
     },
   },
   data() {
@@ -238,14 +238,16 @@ export default {
           contentType: false,
           processData: false,
           success: function (data) {
-            if (data != 'error') {
-              fileName = data.substring(data.indexOf(':') + 1);
+            if(data.errorCode === 0){
+              fileName = data.datas[0];
               const url = URL.createObjectURL(blob);
               _self.$emit('handleStop', {
                 url: url,
                 mblob: blob,
                 fileName: fileName,
               });
+            }else{
+              message.error(data.errorMessage);
             }
           },
           error: function (XMLHttpRequest, textStatus, errorThrown) {

+ 329 - 0
src/widget/EditorWidget.vue

@@ -0,0 +1,329 @@
+<template>
+  <div id="editor" />
+  <!-- eslint-disable-next-line -->
+  <!-- <div id="content-editor-view" class="view-box" v-html="modelValue"/> -->
+  <Loading v-if="loading" />
+</template>
+
+<script>
+import Common from '../common/Common';
+import { message } from 'ant-design-vue';
+import { ajaxApiFile } from '../common/utils.js';
+import { DownloadService } from 'pc-component-v3';
+
+export default {
+  components: {
+    DownloadService,
+  },
+  props: {
+    modelValue: {
+      type: String,
+      default: '',
+    },
+    className: {
+      type: String,
+      default: '',
+    },
+    traceId: {
+      type: String,
+      default: null,
+    },
+  },
+  emits: ['update:modelValue'],
+
+  data: function () {
+    return {
+      loading: false,
+    };
+  },
+
+  watch: {
+    modelValue(newVal) {
+      if ($('#editor').trumbowyg('html') !== newVal) {
+        $('#editor').trumbowyg('html', newVal || '');
+      }
+    },
+  },
+
+  mounted() {
+    const _self = this;
+    // 附件上传
+    $.trumbowyg.plugins.attach = {
+      init: function (trumbowyg) {
+        trumbowyg.addBtnDef('attach', {
+          fn: function () {
+            // 1. 保存当前选区
+            trumbowyg.saveRange();
+
+            const input = document.createElement('input');
+            input.type = 'file';
+            input.style.display = 'none';
+            document.body.appendChild(input);
+            input.click();
+            input.onchange = function () {
+              var file = input.files[0];
+              _self.uploadFile(file, function (fileName) {
+                // 2. 恢复选区
+                trumbowyg.restoreRange();
+                trumbowyg.syncCode(); // 保证内容同步
+                // 3. 插入HTML
+                trumbowyg.execCmd('insertHTML',
+                  `<a class="trumbowyg-attachment-link" class-name="${_self.className}" file-name="${fileName}" style="color:#337ab7;" 
+                  target="_blank">${file.name}</a><span><br></span><br>`,
+                );
+                document.body.removeChild(input);
+              });
+            };
+          },
+          hasIcon: true,
+          ico: 'attach',
+          text: '上传附件',
+          title: '上传附件',
+        });
+      },
+    };
+    $.trumbowyg.plugins.image = {
+      init: function (trumbowyg) {
+        trumbowyg.addBtnDef('image', {
+          title: '图片',
+        });
+      },
+    };
+    // 图片上传
+    $.trumbowyg.plugins.imageUpload = {
+      init: function (trumbowyg) {
+        trumbowyg.addBtnDef('imageUpload', {
+          hasIcon: true,
+          ico: 'upload',
+          text: '上传图片',
+          title: '上传图片',
+          fn: function () {
+            trumbowyg.saveRange();
+            const input = document.createElement('input');
+            input.type = 'file';
+            input.accept = 'image/*';
+            input.style.display = 'none';
+            document.body.appendChild(input);
+            input.click();
+            input.onchange = function () {
+              var file = input.files[0];
+              if (file) {
+                _self.uploadImg(file, function (imagUrl) {
+                  trumbowyg.restoreRange();
+                  trumbowyg.syncCode();
+                  trumbowyg.execCmd('insertHTML', `<img src="${imagUrl}" alt="图片" />`);
+                  document.body.removeChild(input);
+                });
+              } else {
+                document.body.removeChild(input);
+              }
+            };
+          },
+        });
+      },
+    };
+
+    // 初始化编辑器
+    $('#editor').trumbowyg({
+      // svgPath: '../trumbowyg/icons.svg',
+      svgPath: Common.getRootPath() + '/static/trumbowyg/icons/icons.svg',
+      btnsDef: {
+        image: {
+          dropdown: ['insertImage', 'imageUpload'],
+          ico: 'insertImage',
+        },
+      },
+      btns: [
+        // ['viewHTML'],
+        ['formatting'],
+        ['strong', 'em', 'del'],
+        ['link'],
+        'image',
+        ['attach'],
+        ['justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull'],
+        ['unorderedList', 'orderedList'],
+        ['horizontalRule'],
+        ['undo', 'redo'],
+
+        ['fullscreen'],
+      ],
+      autogrow: true,
+      imageWidthModalEdit: true,
+      lang: _self.$i18n.locale == 'zh-CN' ? 'zh_cn' : 'en',
+      plugins: {
+        attach: true,
+        imageUpload: true,
+      },
+    });
+
+    // 粘贴图片特殊处理(粘贴到光标处)
+    $('#editor').on('paste', function (e) {
+      const clipboardData = e.originalEvent.clipboardData;
+      if (!clipboardData) return;
+      let handled = false;
+      
+      // 获取 trumbowyg 实例
+      const trumbowyg = $('#editor').data('trumbowyg');
+      
+      for (let i = 0; i < clipboardData.items.length; i++) {
+        const item = clipboardData.items[i];
+        if (item.type.indexOf('image') !== -1) {
+          const file = item.getAsFile();
+          if (file) {
+            handled = true;
+            
+            // 插入临时文本作为标记
+            const tempId = 'img-placeholder-' + Date.now();
+            if (trumbowyg) {
+              trumbowyg.execCmd('insertText', `[图片上传中${tempId}]`);
+            }
+            
+            // 上传图片
+            _self.uploadImg(file, function (imagUrl) {
+              // 上传完成后,替换临时文本为图片
+              const html = $('#editor').trumbowyg('html');
+              const newHtml = html.replace(`[图片上传中${tempId}]`, `<img src="${imagUrl}" alt="图片" />`);
+              $('#editor').trumbowyg('html', newHtml);
+            });
+          }
+        }
+      }
+      
+      // 如果粘贴的是图片,阻止默认行为
+      if (handled) {
+        e.preventDefault();
+      }
+    });
+
+    // 事件委托,监听点击附件链接下载(编辑器里)
+    // $('#editor').on('click', '.trumbowyg-attachment-link', function (e) {
+    //   e.preventDefault();
+    //   const fileName = $(this).attr('file-name');
+    //   const className = $(this).attr('class-name');
+    //   DownloadService.fileDownload(className, fileName);
+    // });
+
+    // 获取编辑器内容
+    $('#editor').trumbowyg('html', this.modelValue || '');
+
+    // 监听内容变化
+    $('#editor').on('tbwchange', () => {
+      const html = $('#editor').trumbowyg('html');
+      this.$emit('update:modelValue', html);
+    });
+
+    // Common.downloadByA();
+
+  },
+  unmounted() {
+    $('#editor').trumbowyg('destroy');
+  },
+  methods: {
+    // 通过html查看
+    viewByHtml() {
+      var html = $('#editor').trumbowyg('html');
+      $('#content-editor-view').html(html);
+    },
+
+    // 上传图片
+    async uploadImg(file, insertFn) {
+      console.log(file);
+      const _self = this;
+      const formData = new FormData();
+      try {
+        formData.append('images', file);
+        formData.append('className', _self.className);
+        const url = 'file/imageUpload';
+        _self.loading = true;
+        await ajaxApiFile(url, formData).then(res => {
+          if (res.errorCode === 0) {
+            const imageName = res.datas[0];
+            const imagUrl = Common.getResourceUrl1('image', imageName);
+            insertFn(imagUrl);
+          } else {
+            message.warning(res.errorMessage);
+          }
+          _self.loading = false;
+        });
+      } catch (error) {
+        _self.loading = false;
+        console.error('上传图片失败:', error);
+        message.error('图片上传失败,请稍后重试');
+      }
+    },
+    /**
+     * 上传文件
+     * @param  {File} selectedFile 选择的文件
+     */
+    uploadFile: function (selectedFile, callback) {
+      var _self = this;
+      if (selectedFile == undefined) {
+        return;
+      }
+      //附件大小时默认8m
+      var size = 8;
+
+      if (selectedFile.size / 1024 <= 1024 * size) {
+        var formData = new FormData();
+        formData.append('files', selectedFile);
+        formData.append('className', _self.className);
+        _self.loading = true;
+        $.ajax({
+          url: Common.getApiURL('file/classFileUpload'),
+          type: 'post',
+          beforeSend: function (request) {
+            Common.addTokenToRequest(request);
+          },
+          data: formData,
+          contentType: false,
+          processData: false,
+          success: function (data) {
+            _self.loading = false;
+            if (data.errorCode == 0) {
+              if (data.datas) {
+                var fileName = data.datas[0];
+                if (callback) callback(fileName);
+              }
+            } else {
+              message.error(data.errorMessage);
+            }
+          },
+          error: function (XMLHttpRequest, textStatus, errorThrown) {
+            _self.loading = false;
+            Common.processException(XMLHttpRequest, textStatus, errorThrown);
+          },
+        });
+      } else {
+        _self.loading = false;
+        Notify.error(
+          '提示',
+          '文件大小不能超过' + size,
+        );
+      }
+    },
+  },
+
+
+};
+</script>
+
+<style>
+.trumbowyg-box .trumbowyg-editor {
+  height: 368px !important;
+}
+
+.trumbowyg-button-pane {
+  background: white !important;
+  border-bottom: none !important;
+}
+
+.trumbowyg-editor {
+  padding: 6px 14px !important;
+}
+
+.view-box {
+  border: 1px solid #ccc;
+  min-height: 200px;
+  padding: 8px;
+}
+</style>

+ 78 - 42
src/widget/GanttScale2.vue

@@ -1,47 +1,67 @@
 <template>
   <div>
-    <div class="radio">
-      <label class="radio-inline" />
-      <div class="btn-group btn-link pull-left">
-        <button v-if="canEdit" type="button" class="btn btn-default fa fa-floppy-o" @click="runSaveGantt">&nbsp;保存</button>
-        <button type="button" class="btn btn-default fa fa-refresh" @click="showTaskDtos">&nbsp;刷新</button>
-        <button type="button" class="btn btn-default fa fa-search-plus" :disabled="disableEnlarge" @click="scale++">&nbsp;放大</button>
-        <button type="button" class="btn btn-default fa fa-search-minus" :disabled="disableReduction" @click="scale--">&nbsp;缩小</button>
-        <button type="button" class="btn btn-default fa fa-search" @click="setDefaultScale">&nbsp;默认</button>
-        <button v-if="hideButtons != true" type="button" class="btn btn-default" @click="showGanttChanged">
+    <a-space>
+      <a-button-group>
+        <a-button @click="runSaveGantt">
+          <save-outlined />
+          保存
+        </a-button>
+        <a-button @click="showTaskDtos">
+          <reload-outlined />
+          刷新
+        </a-button>
+        <a-button :disabled="disableEnlarge" @click="scale++">
+          <zoom-in-outlined />
+          放大
+        </a-button>
+        <a-button :disabled="disableReduction" @click="scale--">
+          <zoom-out-outlined />
+          缩小
+        </a-button>
+        <a-button @click="setDefaultScale">
+          <search-outlined />
+          默认
+        </a-button>
+        <a-button v-if="hideButtons != true" @click="showGanttChanged">
           <span v-if="showGantt == 1">手动排程</span>
           <span v-if="showGantt == 2">自动排程</span>
-        </button>
-        <button type="button" class="btn btn-default fa fa-undo" @click="undo">&nbsp;上一步</button>
-        <button type="button" class="btn btn-default fa fa-repeat" @click="redo">&nbsp;下一步</button>
-        <button id="fullscreen_button" type="button" class="btn btn-default fa fa-arrows-alt" @click="expand">&nbsp;全屏</button>
-        <button v-if="canEdit" type="button" class="btn btn-default" @click="importTemplate">导入模板</button>
-        <button type="button" class="btn btn-default" @click="exportFile">导出</button>
-      </div>
-      <!-- 			<div id="output">
-				选择的网站是: {{selected}}
-			</div> -->
-      <Modal v-model:show="create" large="true" @cancel="$refs.create.show = false" @ok="importTest">
-        <template #header>
-          选择模板进行导入
-        </template>
-        <div>
-          <div class="form-horizontal">
-            <div class="form-group">
-              <label for="ganttName" class="col-xs-4 col-sm-3 col-md-2 col-lg-1 control-label">
-                <nobr>选择模板</nobr>
-              </label>
-              <div class="col-xs-8 col-sm-9 col-md-10 col-lg-11">
-                <select v-model="selected" class="form-control">
-                  <option value="">选择一个模板</option>
-                  <option v-for="template in templates" :key="template">{{ template }}</option>
-                </select>
-              </div>
-            </div>
-          </div>
-        </div>
-      </Modal>
-    </div>
+        </a-button>
+        <a-button @click="undo">
+          <undo-outlined />
+          上一步
+        </a-button>
+        <a-button @click="redo">
+          <redo-outlined />
+          下一步
+        </a-button>
+        <a-button id="fullscreen_button" @click="expand">
+          <fullscreen-outlined />
+          全屏
+        </a-button>
+        <a-button @click="importTemplate">
+          导入模板
+        </a-button>
+        <a-button @click="exportFile">
+          导出
+        </a-button>
+      </a-button-group>
+    </a-space>
+
+    <a-modal
+      v-model:open="create"
+      title="选择模板进行导入"
+      width="600px"
+      @ok="importTest"
+    >
+      <a-form layout="horizontal">
+        <a-form-item label="选择模板" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
+          <a-select v-model:value="selected" style="width: 100%">
+            <a-select-option value="">选择一个模板</a-select-option>
+            <a-select-option v-for="template in templates" :key="template">{{ template }}</a-select-option>
+          </a-select>
+        </a-form-item>
+      </a-form>
+    </a-modal>
   </div>
 </template>
 
@@ -52,12 +72,28 @@ import Common from '../common/Common.js';
 
 // import gantt from 'gantt';
 import { Notify } from 'pc-component-v3';
-
+import {
+  SaveOutlined,
+  ReloadOutlined,
+  ZoomInOutlined,
+  ZoomOutOutlined,
+  SearchOutlined,
+  UndoOutlined,
+  RedoOutlined,
+  FullscreenOutlined,
+} from '@ant-design/icons-vue';
 
 export default {
 
   components: {
-    
+    SaveOutlined,
+    ReloadOutlined,
+    ZoomInOutlined,
+    ZoomOutOutlined,
+    SearchOutlined,
+    UndoOutlined,
+    RedoOutlined,
+    FullscreenOutlined,
     
   },
   /**

+ 22 - 16
src/widget/QueryWidget.vue

@@ -5,14 +5,21 @@
 -->
 <template>
   <div class="m-container">
-    <div class="input-group">
-      <input v-model="searchText" autocomplete="off" type="text" class="form-control" :placeholder="placeholderText" @keyup.enter="search" />
-      <span class="input-group-btn">
-        <button style="width:100%;" type="button" class="btn btn-blue search-button" @click="search()">
-          查询
-        </button>
-      </span>
-    </div>
+    <a-input-group compact>
+      <a-input 
+        v-model:value="searchText" 
+        :placeholder="placeholderText" 
+        style="width: calc(100% - 80px)"
+        @keyup.enter="search"
+      />
+      <a-button 
+        type="primary" 
+        style="width: 80px"
+        @click="search"
+      >
+        查询
+      </a-button>
+    </a-input-group>
   </div>
 </template>
 
@@ -34,11 +41,11 @@ export default {
     };
   },
 		
-  watch: {
-    'searchText' : function(curVal, oldVal){
-      this.$emit('valueChanged', curVal);
-    },
-  },
+  // watch: {
+  //   'searchText' : function(curVal, oldVal){
+  //     this.$emit('valueChanged', curVal);
+  //   },
+  // },
 		
   methods: {
     /**
@@ -67,8 +74,7 @@ export default {
 </script>
 
 <style scoped>
-	button{
-		background-color: #007aff;
-		color: white;
+	.m-container {
+    width: 100%;
 	}
 </style>

+ 2 - 2
webpack.base.js

@@ -43,14 +43,14 @@ module.exports = {
             },
           },
           generator: {
-            filename: './client-trace-v3-image/[name][ext][query]',
+            filename: './client-trace-v5-image/[name][ext][query]',
           },
         },
         {
           test: /\.(eot|woff|woff2|ttf)$/,
           type: 'asset/resource',
           generator: {
-            filename: './client-trace-v3-font/[name].[ext]?[hash]',
+            filename: './client-trace-v5-font/[name].[ext]?[hash]',
           },
         },
       ]

+ 13 - 13
webpack.dev.js

@@ -19,8 +19,8 @@ module.exports = WebpackMerge.merge(baseConfig, {
   output: {
     path: path.resolve(__dirname, '../dist'),
     publicPath: '/',
-    filename: 'client-trace-v3-[name].js',
-    chunkFilename: 'client-trace-v3-chunk-[name].js',
+    filename: 'client-trace-v5-[name].js',
+    chunkFilename: 'client-trace-v5-chunk-[name].js',
   },
 
   watchOptions: {
@@ -33,65 +33,65 @@ module.exports = WebpackMerge.merge(baseConfig, {
     compress: false,
     proxy: {
       '/api': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: false,
         changeOrigin: true,
         secure:true,
       },
       '/static': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: false,
         changeOrigin: true,
         secure:true,
       },
       '/content': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: false,
         changeOrigin: true,
         secure:true,
       },
       '/dashboard': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: false,
         changeOrigin: true,
         secure:true,
       },
       '/mock': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: false,
         changeOrigin: true,
         secure:true,
       },
       '/authApi': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: false,
         changeOrigin: true,
         secure:true,
       },
       '/Dictionary': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: false,
         changeOrigin: true,
         secure:true,
       },
       '/Files': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: false,
         changeOrigin: true,
         secure:true,
       },
       '/WebSocket': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: true,
         changeOrigin: true,
       },
       '/TrainVideo': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: true,
         changeOrigin: true,
       },
       '/gateway-api': {
-        target: 'http://localhost:10022/',
+        target: 'https://a.leanwo.com/',
         ws: true,
         changeOrigin: true,
         secure:true,

+ 3 - 3
webpack.lib.js

@@ -17,11 +17,11 @@ module.exports = WebpackMerge.merge(baseConfig,{
   output: {
     path: path.resolve(__dirname, './dist'),
     publicPath: '/dist/',
-    filename: 'client-trace-v3.js',
+    filename: 'client-trace-v5.js',
     // library: {
     //   type: 'module',
     // },
-    library: 'client-trace-v3',
+    library: 'client-trace-v5',
     libraryTarget: 'umd',
     //「devtool 中模块」的文件名模板(用于冲突)
     umdNamedDefine: false,
@@ -58,7 +58,7 @@ module.exports = WebpackMerge.merge(baseConfig,{
     
     // CSS 提取
     new MiniCssExtractPlugin({
-      filename: 'client-trace-v3.css',
+      filename: 'client-trace-v5.css',
     }),
 
     // 打包分析插件

+ 3 - 3
webpack.prod.js

@@ -23,8 +23,8 @@ module.exports =  WebpackMerge.merge(baseConfig, {
   output: {
     path: path.resolve(__dirname, './dist'),
     publicPath: './',
-    filename: './client-trace-v3-bundle/[name].[contenthash:8].js',
-    chunkFilename: './client-trace-v3-chunk/[name].[contenthash:8].js',
+    filename: './client-trace-v5-bundle/[name].[contenthash:8].js',
+    chunkFilename: './client-trace-v5-chunk/[name].[contenthash:8].js',
   },
 
 
@@ -75,7 +75,7 @@ module.exports =  WebpackMerge.merge(baseConfig, {
 
     // CSS 提取
     new MiniCssExtractPlugin({
-      filename: "./client-trace-v3-style/[name].[contenthash:8].css"
+      filename: "./client-trace-v5-style/[name].[contenthash:8].css"
     }),
 
   ])