index.vue 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758
  1. <template>
  2. <div v-loading="loading.drawer" class="bug-detail">
  3. <div v-if="type!=='page'" style="padding: 15px 5px 5px 30px" class="Layout_space_between bug_manage_container bug_manage_title">
  4. <div class="Layout_flex_start">
  5. <span style="font-size:18px;font-weight:500;color:#333b4a; margin-right: 20px;">{{ 'BUG-'+ id }}</span>
  6. <statusChange :status-code="bug.status" :bug-data="bug" :status-obj="statusObj" @bugGet="updateBugStatus" />
  7. </div>
  8. <div style="display: inline-block;float:right">
  9. <span class="newBtn" @click="getToDetails()"><i class="el-icon-document" /> 查看详情</span>
  10. <span class="newBtn" style="padding: 0 10px" @click="openQueryDialog()"><i class="el-icon-document-copy" /> 复制</span>
  11. <span class="newBtn" style="padding-right: 10px;" @click="openDeleteDialog()"><i class="el-icon-delete" /> 删除</span>
  12. <div style="display: inline-block;margin-left: 10px;margin-right: 15px;" @mouseover="iconName = 'float_反馈_icon_close_蓝色'" @mouseleave="iconName = 'float_反馈_icon_close'" @mousedown="closeDrawer();">
  13. <el-button size="medium" type="text" style="margin-top:-5px;">
  14. <svg-icon :icon-class="iconName" />
  15. </el-button>
  16. </div>
  17. </div>
  18. </div>
  19. <el-container
  20. v-loading.fullscreen.lock="loading.fullscreen"
  21. :style="type=='page'?{'background-color': '#F2F3F6','padding': '0 0 0 10px'}:{'background-color': '#ffffff','overflow': 'auto','height':height}"
  22. class="bug_manage_container scop"
  23. >
  24. <el-header height="0" />
  25. <el-container>
  26. <el-main v-loading="loading.title" class="layout_main bug_manage_title" :style="type=='page'?{'padding': '15px 30px 15px 30px'}:{'padding':'0px 30px 15px 15px'}">
  27. <span id="spanLength">{{ bugNameForm.bugName }}</span>
  28. <el-row v-if="type=='page'">
  29. <el-form
  30. label-position="right"
  31. :inline="true"
  32. :model="bugNameForm"
  33. :rules="rules"
  34. :hide-required-asterisk="true"
  35. >
  36. <el-col id="divLength" :span="24" style="padding: 0">
  37. <el-form-item :label="bug.bugId" prop="bugName" style="margin:0;margin-bottom:0px">
  38. <el-input
  39. v-model="bugNameForm.bugName"
  40. type="textarea"
  41. :autosize="{ minRows: 1, maxRows: 3}"
  42. maxlength="150"
  43. :show-word-limit="showWordLimit"
  44. :style="bugNameTextareaStyle"
  45. :class="!bugNameForm.bugName && showRule? 'bug_manage_bug_name_rule_show':bugNameIsFocus?'bug_manage_bug_name_focus':'bug_manage_bug_name'"
  46. @blur="showWordLimit = false;bugNameIsFocus = false;changeBugName()"
  47. @focus="showWordLimit = true;bugNameIsFocus = true;changeWidthOnFocus()"
  48. @input="changeRule()"
  49. @keydown.enter.native="listen($event)"
  50. />
  51. </el-form-item>
  52. <el-form-item id="itemLength1" style="margin:0">
  53. <statusChange :status-code="bug.status" :bug-data="bug" :status-obj="statusObj" @bugGet="updateBugStatus" />
  54. </el-form-item>
  55. <el-form-item id="itemLength2" style="margin:0;float:right">
  56. <el-button size="small" class="button_delete2" @click="openDeleteDialog()">删除</el-button>
  57. </el-form-item>
  58. </el-col>
  59. </el-form>
  60. </el-row>
  61. <div v-else>
  62. <div>
  63. <el-form
  64. :model="bugNameForm"
  65. :rules="rules"
  66. >
  67. <el-form-item prop="bugName">
  68. <el-input
  69. v-model="bugNameForm.bugName"
  70. type="textarea"
  71. autosize
  72. maxlength="150"
  73. :show-word-limit="showWordLimit"
  74. style="width:100%;"
  75. :class="!bugNameForm.bugName && showRule? 'bug_manage_bug_name_rule_show':bugNameIsFocus?'bug_manage_bug_name_focus':'bug_manage_bug_name'"
  76. @blur="showWordLimit = false;bugNameIsFocus=false;changeBugName()"
  77. @focus="showWordLimit = true;bugNameIsFocus=true;"
  78. @input="changeRule()"
  79. @keydown.enter.native="listen($event)"
  80. />
  81. </el-form-item>
  82. </el-form>
  83. </div>
  84. </div>
  85. </el-main>
  86. </el-container>
  87. <el-container ref="box" :style="type=='page'?{'margin-top':'10px'}:{}" class="bug_manage" :direction="type == 'page'?'horizontal':'vertical'">
  88. <el-aside :style="type=='page'?{'margin-right':'10px','width':'70%'}:{'width':'100%'}">
  89. <el-main v-loading="loading.details" class="layout_main bug-contain" :style="type=='page'?{'padding': '20px 30px'}:{'padding-left': '30px','padding-right': '30px'}">
  90. <div class="module_title">
  91. <div class="module_title__sign" />
  92. <div class="module_title__caption">缺陷详情</div>
  93. </div>
  94. <el-divider v-if="type !== 'page'" />
  95. <el-container width="100%">
  96. <el-aside width="49%" style="margin-right: 2%">
  97. <el-form label-width="30%" label-position="left" label-suffix=":">
  98. <el-form-item label="所属任务" class="limit-font">
  99. <span v-if="clielIcon === true" class="str-task-name" @mouseover="mouseOver" @mouseleave="mouseLeave"><span @click="JumpTask(bug.taskId)">{{ bug.taskName }}</span> <i v-if="active" class="el-icon-edit" @click="getclielIcon" /></span>
  100. <el-select
  101. v-if="clielIcon === false"
  102. ref="searchOfChatRoom"
  103. v-model="bug.taskId"
  104. filterable
  105. :remote="true"
  106. :remote-method="debounceQuery"
  107. @visible-change="mouseLeaves"
  108. @change="bugUpdates(bug,'details')"
  109. >
  110. <el-option v-for="item in taskEnumList" :key="item.id" :label="item.name" :value="item.id">
  111. <div class="belong-task">
  112. <div class="task-id">{{ item.taskId }}</div>
  113. <div class="modules-name">
  114. <span class="name">{{ item.name }}</span>
  115. <span v-if="item.moduleInfoName" class="modules">{{ item.moduleInfoName }}</span>
  116. </div>
  117. </div>
  118. </el-option>
  119. </el-select>
  120. </el-form-item>
  121. <el-form-item label="优先级">
  122. <el-select v-model="bug.priorityLevel" @change="bugUpdate(bug,'details')">
  123. <el-option
  124. v-for="item in enums.priorityLevelEnumList"
  125. :key="item.code"
  126. :label="item.name"
  127. :value="item.name"
  128. />
  129. </el-select>
  130. </el-form-item>
  131. <el-form-item label="业务线">
  132. <div style="padding-left: 15px;color: #666666; font-weight: 500">{{ bug.bizName }}</div>
  133. </el-form-item>
  134. <el-form-item label="发现阶段">
  135. <el-select v-model="bug.discoveryStage" @change="bugUpdate(bug,'details')">
  136. <el-option
  137. v-for="item in enums.bugStageEnumList"
  138. :key="item.code"
  139. :label="item.name"
  140. :value="item.code"
  141. />
  142. </el-select>
  143. </el-form-item>
  144. <el-form-item label="端类型">
  145. <el-select v-model="bug.sysType" @change="bugUpdate(bug,'details')">
  146. <el-option
  147. v-for="item in enums.sysTypeEnumList"
  148. :key="item.code"
  149. :label="item.name"
  150. :value="item.code"
  151. />
  152. </el-select>
  153. </el-form-item>
  154. <!-- <el-form-item label="缺陷原因">
  155. <div v-if="map.bugReasonEnumList && bug" class="bug_manage_div">{{ map.bugReasonEnumList[bug.bugReason]?map.bugReasonEnumList[bug.bugReason]:'尚未知' }}</div>
  156. </el-form-item> -->
  157. <el-form-item label="Reopen次数">
  158. <div class="bug_manage_div">{{ bug.reopenTimes }}</div>
  159. </el-form-item>
  160. <el-form-item label="Hold次数">
  161. <div class="bug_manage_div">{{ bug.holdCount }}</div>
  162. </el-form-item>
  163. <el-form-item label="标签">
  164. <Tag v-model="bug.tags" type="BUG" @change="bugUpdate(bug,'details')" />
  165. </el-form-item>
  166. </el-form>
  167. </el-aside>
  168. <el-aside width="49%">
  169. <el-form label-width="30%" label-position="left" label-suffix=":">
  170. <el-form-item label="所属需求" class="limit-font">
  171. <span v-if="bug.requirementName !== null" class="str-task-name" @click="JumpRequire(bug.requireId)">{{ bug.requirementName }}</span>
  172. <span v-else class="str-task-name">{{ '无归属需求' }}</span>
  173. </el-form-item>
  174. <el-form-item label="缺陷等级">
  175. <el-select v-model="bug.priority" @change="bugUpdate(bug,'details')">
  176. <el-option
  177. v-for="item in enums.priorityEnumList"
  178. :key="item.code"
  179. :label="item.name"
  180. :value="item.code"
  181. />
  182. </el-select>
  183. </el-form-item>
  184. <el-form-item label="所属模块">
  185. <el-cascader v-model="bug.moduleIds" class="current" collapse-tags :props="props" :options="business_platform_Modular" placeholder="请选择" @change="bugUpdate(bug,'details')" @click.native="getBusinessLinePlatformModule" />
  186. </el-form-item>
  187. <el-form-item label="发现方式">
  188. <el-select v-model="bug.discoveryMeth" @change="bugUpdate(bug,'details')">
  189. <el-option
  190. v-for="item in enums.discoveryMethEnumList"
  191. :key="item.code"
  192. :label="item.name"
  193. :value="item.code"
  194. />
  195. </el-select>
  196. </el-form-item>
  197. <el-form-item label="缺陷类型">
  198. <el-cascader
  199. v-model="bug.theBugType"
  200. :options="theBugTypeEnumList"
  201. :props="{
  202. value:'code',
  203. label:'name',
  204. children: 'childrenEnums',
  205. emitPath: false
  206. }"
  207. placeholder="请选择"
  208. @change="bugUpdate(bug,'details')"
  209. />
  210. </el-form-item>
  211. <el-form-item label="修复结果">
  212. <div
  213. v-if="map.repairResultEnumList && bug"
  214. class="bug_manage_div"
  215. >{{ map.repairResultEnumList[bug.repairResult]?map.repairResultEnumList[bug.repairResult]:'未修复' }}</div>
  216. </el-form-item>
  217. </el-form>
  218. </el-aside>
  219. </el-container>
  220. </el-main>
  221. <el-main v-loading="loading.appInfo" :style="type=='page'?{'padding': '20px 30px 20px 30px','margin-top': '10px'}:{'padding-left':'30px','padding-right':'30px'}" class="layout_main bug_manage bug_manage_app_info">
  222. <div class="module_title">
  223. <div class="module_title__sign" />
  224. <div class="module_title__caption">客户端信息</div>
  225. </div>
  226. <el-divider v-if="type !== 'page'" />
  227. <el-container width="100%">
  228. <el-aside width="49%" style="margin-right: 2%">
  229. <el-form label-width="30%" label-position="left" label-suffix=":">
  230. <el-form-item label="客户端">
  231. <el-select
  232. v-model="bug.appId"
  233. clearable
  234. filterable
  235. @change="getVersionList(bug.appId), bugUpdate(bug,'appInfo');"
  236. >
  237. <el-option
  238. v-for="item in appClientList"
  239. :key="item.code"
  240. :label="item.msg"
  241. :value="item.code"
  242. />
  243. </el-select>
  244. </el-form-item>
  245. <el-form-item label="机型">
  246. <el-input v-model="bug.osType" placeholder="请输入" @change="bugUpdate(bug,'appInfo')" />
  247. </el-form-item>
  248. <el-form-item label="网络">
  249. <el-select v-model="bug.networkType" @change="bugUpdate(bug,'appInfo')">
  250. <el-option
  251. v-for="item in enums.networkTypeEnumList"
  252. :key="item.code"
  253. :label="item.name"
  254. :value="item.name"
  255. />
  256. </el-select>
  257. </el-form-item>
  258. </el-form>
  259. </el-aside>
  260. <el-aside width="49%">
  261. <el-form label-width="30%" label-position="left" label-suffix=":">
  262. <el-form-item label="影响版本">
  263. <el-select
  264. v-model="bug.appVersion"
  265. clearable
  266. filterable
  267. style="width:100%;"
  268. @change="bugUpdate(bug,'appInfo')"
  269. >
  270. <el-option
  271. v-for="item in versionList"
  272. :key="item.code"
  273. :label="item.msg"
  274. :value="item.msg"
  275. />
  276. </el-select>
  277. </el-form-item>
  278. <el-form-item label="系统版本">
  279. <el-input v-model="bug.sdkVersion" placeholder="请输入" @change="bugUpdate(bug,'appInfo')" />
  280. </el-form-item>
  281. </el-form>
  282. </el-aside>
  283. </el-container>
  284. </el-main>
  285. </el-aside>
  286. <el-aside :width="type=='page'?'29.2%':'100%'">
  287. <el-main v-loading="loading.userInfo" :style="type=='page'?{'padding': '20px 30px'}:{'padding-left':'30px'}" class="layout_main bug_manage_user_info">
  288. <div class="module_title">
  289. <div class="module_title__sign" />
  290. <div class="module_title__caption">用户信息</div>
  291. </div>
  292. <el-divider v-if="type !== 'page'" />
  293. <el-aside :width="type=='page'?'100%':'100%'">
  294. <el-form :rules="rules" :label-width="type=='page'?'30%':'16%'" label-position="left" label-suffix=":" :hide-required-asterisk="true">
  295. <el-form-item label="提报人">
  296. <div class="bug_manage_div">{{ bug.creatorList }}</div>
  297. </el-form-item>
  298. <el-form-item label="责任人" prop="assigner" :style="formHeight1 == 'type' ? { 'height': '60px' } : { 'height': 'auto' }">
  299. <search-people :value.sync="bug.assigner" :multiple="true" @change="bugUpdate(bug,'userInfo')" />
  300. </el-form-item>
  301. <el-form-item label="修复人" prop="currentHandler" :style="formHeight == 'type' ? { 'height': '60px' } : { 'height': 'auto' }">
  302. <search-people :value.sync="bug.currentHandler" :multiple="true" @change="bugUpdate(bug,'userInfo')" />
  303. </el-form-item>
  304. </el-form>
  305. </el-aside>
  306. </el-main>
  307. <el-main class="layout_main bug_manage_time_length" :style="type=='page'?{'padding': '20px 5px 20px 30px','margin-top': '10px'}:{'padding-left':'30px'}">
  308. <div class="module_title">
  309. <div class="module_title__sign" />
  310. <div class="module_title__caption">时间</div>
  311. </div>
  312. <el-divider v-if="type !== 'page'" />
  313. <el-aside :width="'100%'">
  314. <el-form label-position="left" label-width="100px" :class="type !== 'page'&& 'timeForm'">
  315. <el-form-item label="提报时间">
  316. <div class="bug_manage_div"> :{{ bug.gmtCreate }} <span v-if="bug.gmtCreateIsHoliday === 1" class="bug_date">{{ '节假日提报' }}</span></div>
  317. </el-form-item>
  318. <el-form-item label="待测试时间">
  319. <div class="bug_manage_div"> :{{ bug.waitTestTime }}</div>
  320. </el-form-item>
  321. <el-form-item label="已完成时间">
  322. <div class="bug_manage_div"> :{{ bug.finishTime }} <span v-if="bug.finishTimeIsHoliday === 1" class="bug_date">{{ '节假日关闭' }}</span></div>
  323. </el-form-item>
  324. <el-form-item label="开发修复时长">
  325. <div class="bug_manage_div">
  326. <!-- :{{ bug.devFixTime }} <br> -->
  327. :{{ bug.devFixTimePurgeNH }} <span v-if="bug.devFixTimePurgeNH !== null" class="bug_removeDate">{{ '去除节假日' }}</span>
  328. </div>
  329. </el-form-item>
  330. <el-form-item label="测试验收时长">
  331. <div class="bug_manage_div">
  332. <!-- :{{ bug.testCheckTime }} <br> -->
  333. :{{ bug.testCheckTimePurgeNH }} <span v-if="bug.testCheckTimePurgeNH !== null" class="bug_removeDate">{{ '去除节假日' }}</span>
  334. </div>
  335. </el-form-item>
  336. <el-form-item label="总修复时长">
  337. <div class="bug_manage_div">
  338. <!-- :{{ bug.fixTime }} <br> -->
  339. :{{ bug.fixTimePurgeNH }} <span v-if="bug.fixTimePurgeNH !== null" class="bug_removeDate">{{ '去除节假日' }}</span>
  340. </div>
  341. </el-form-item>
  342. </el-form>
  343. </el-aside>
  344. </el-main>
  345. </el-aside>
  346. </el-container>
  347. <el-container :style="type=='page'?{'width':'70%'}:{'width':'100%'}" direction="vertical">
  348. <el-main v-loading="loading.describe" :style="type=='page'?{'padding': '20px 30px','margin-top': '10px'}:{'padding-left':'30px'}" class="layout_main">
  349. <div class="module_title">
  350. <div class="module_title__sign" />
  351. <div class="module_title__caption">描述</div>
  352. </div>
  353. <el-divider v-if="type !== 'page'" />
  354. <div
  355. v-if="!bug.bugDescribe && describeEditorVisible == false"
  356. style="width: 100%;height: 300px;text-align: center;line-height: 300px"
  357. >
  358. <span class="bug_describe" @click="describeEditorVisible = true;">点击添加描述</span>
  359. </div>
  360. <el-tooltip
  361. v-if="bug.bugDescribe && describeEditorVisible == false"
  362. effect="dark"
  363. content="点击编辑"
  364. placement="top"
  365. >
  366. <div class="bug_describe_content" @click="describeEditorVisible = true;">
  367. <div style="font-size:14px" v-html="bug.bugDescribe" />
  368. </div>
  369. </el-tooltip>
  370. <div v-show="describeEditorVisible" style="margin-top:15px">
  371. <!-- <editor v-model="text_content" class="tinymce" :init="configure" @blur="describeConfirm" /> -->
  372. <normal-area
  373. id="buglist_tinymce"
  374. :value.sync="text_content"
  375. :height="500"
  376. />
  377. <div style="margin-top:40px;float: right">
  378. <el-button @click="describeCancel()">取 消</el-button>
  379. <el-button type="primary" @click="describeConfirm()">确 认</el-button>
  380. </div>
  381. </div>
  382. </el-main>
  383. <el-main
  384. :style="type=='page'?{'padding': '20px 30px','margin-top': '10px'}:{'padding-left':'30px'}"
  385. class="layout_main"
  386. >
  387. <div class="module_title">
  388. <div class="module_title__sign" />
  389. <div class="module_title__caption upload-title">附件</div>
  390. <div class="upload-file"><i class="el-icon-info" />支持直接截图黏贴图片</div>
  391. </div>
  392. <el-divider v-if="type !== 'page'" />
  393. <section class="upload-main">
  394. <el-tooltip
  395. class="item"
  396. effect="dark"
  397. content="支持的文件格式有:.zip, .xlsx, .txt, .csv, .xls, .mov, .mp4, .m4a, .avi, .amr, .mp3, .wav, .3gpp, .png, .jpg, .jpeg, .gif"
  398. placement="top-start"
  399. popper-class="tip-style"
  400. >
  401. <div class="upload-info"><i class="el-icon-question" />格式说明</div>
  402. </el-tooltip>
  403. <el-upload
  404. ref="upload"
  405. class="upload-demo"
  406. action="https://star.xiaojukeji.com/upload/img.node"
  407. :on-preview="handleDownload"
  408. :before-upload="beforeUpload"
  409. :before-remove="beforeRemove"
  410. :on-remove="handleRemoveNotImage"
  411. :on-success="handleChange"
  412. :on-error="errorChange"
  413. multiple
  414. accept="*"
  415. :file-list="notImageList"
  416. >
  417. <el-button slot="trigger" icon="el-icon-upload" class="upload-button">上传附件</el-button>
  418. </el-upload>
  419. <el-upload
  420. style="margin-top: 15px"
  421. class="upload-demo"
  422. list-type="picture-card"
  423. multiple
  424. accept="*"
  425. :file-list="ImageList"
  426. action="https://star.xiaojukeji.com/upload/img.node"
  427. >
  428. <el-button v-if="false" slot="trigger" size="small" type="primary">上传附件</el-button>
  429. <div slot="file" slot-scope="{file}" class="image-detail">
  430. <el-tooltip
  431. class="item"
  432. effect="dark"
  433. :content="file.name"
  434. placement="top-start"
  435. popper-class="tip-style"
  436. >
  437. <div class="image-name">{{ file.name }}</div>
  438. </el-tooltip>
  439. <img class="el-upload-list__item-thumbnail detail-img" :src="file.url" alt="">
  440. <span class="el-upload-list__item-actions">
  441. <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
  442. <i class="el-icon-zoom-in" />
  443. </span>
  444. <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleDownload(file)">
  445. <i class="el-icon-download" />
  446. </span>
  447. <span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemoveImage(file)">
  448. <i class="el-icon-delete" />
  449. </span>
  450. </span>
  451. </div>
  452. </el-upload>
  453. <el-image-viewer v-if="dialogVisible" :on-close="closeViewer" :url-list="srcList" style="z-index: 999999;" />
  454. </section>
  455. </el-main>
  456. <el-main :style="type=='page'?{'padding': '20px 30px','margin-top': '10px'}:{'padding-left':'30px'}" class="layout_main">
  457. <div class="module_title">
  458. <div class="module_title__sign" />
  459. <div class="module_title__caption">动态</div>
  460. </div>
  461. <el-divider v-if="type !== 'page'" />
  462. <el-tabs v-model="activeName" @tab-click="handleClick">
  463. <el-tab-pane label="评论" name="first">
  464. <div>
  465. <div v-for="(item,index) in comments" :key="index" class="animated bounceInRight">
  466. <div style="font-size:14px;color:#333B4A;display: inline-block;">{{ item.commentInfo.name }}</div>
  467. <div style="margin-left:20px;display: inline-block;color: #9B9B9B;font-size:12px">{{ item.commentInfo.gmtCreater }}</div>
  468. <p style="font-size:14px;color:#333B4A;margin-top: 10px;white-space: pre-line;">{{ item.commentInfo.content }}</p>
  469. <br>
  470. </div>
  471. <el-input
  472. v-model="commentContent"
  473. type="textarea"
  474. placeholder="请输入评论内容"
  475. maxlength="1000"
  476. show-word-limit
  477. :autosize="{ minRows: 3, maxRows: 5}"
  478. style="margin-bottom: 2%"
  479. />
  480. <el-button type="primary" size="small" style="float: right" @click="addComment">发表评论</el-button>
  481. </div>
  482. </el-tab-pane>
  483. <el-tab-pane label="变更记录" name="second">
  484. <div>
  485. <div v-for="(item,index) in changeRecord" :key="index" style="margin: 20px 0" class="animated bounceInRight Layout_space_between">
  486. <span class="operatorName">{{ item.operator }}</span>
  487. <el-tooltip
  488. v-if="item.remark && item.remark.length > 25"
  489. class="item"
  490. effect="dark"
  491. :content="item.remark"
  492. placement="top-start"
  493. popper-class="tip-style"
  494. >
  495. <span class="remark">{{ item.remark && item.remark.length > 25 ? item.remark.substring(0, 30) + '...' : item.remark }}</span>
  496. </el-tooltip>
  497. <span v-else class="remark">{{ item.remark }}</span>
  498. <span class="createTime">{{ item.createTime }}</span>
  499. </div>
  500. </div>
  501. </el-tab-pane>
  502. </el-tabs>
  503. </el-main>
  504. </el-container>
  505. <!-- 弹窗 -->
  506. <el-dialog
  507. v-if="statusDialogVisible"
  508. :visible.sync="statusDialogVisible"
  509. width="30%"
  510. title="删除确认"
  511. class="public_task"
  512. :append-to-body="true"
  513. :close-on-click-modal="false"
  514. >
  515. <div class="blueStripe" />
  516. <div style="text-align:center;">是否要删除当前缺陷?</div>
  517. <template v-slot:footer>
  518. <el-button size="mini" @click="statusDialogVisible = false">取 消</el-button>
  519. <el-button size="mini" type="primary" @click="statusDialogConfirm">确 定</el-button>
  520. </template>
  521. </el-dialog>
  522. <createdBug v-if="modalShow" ref="createdBug" @father="father" />
  523. <normal-dialog
  524. :show-dialog="showCopyFile"
  525. :title="'上传截图'"
  526. :width="'40%'"
  527. :submit-button="'上传'"
  528. :top="'5vh'"
  529. @confirm="confirmUpload()"
  530. @cancel="showCopyFile=false"
  531. >
  532. <div class="file-dialog">
  533. <el-form ref="imageForm" label-width="20%" :rules="imageRules" :model="imageName">
  534. <el-form-item label="图片命名" prop="name">
  535. <el-col style="width: 75%">
  536. <el-input v-model="imageName.name" placeholder="请输入图片名称" />
  537. </el-col>
  538. <el-col style="width: 10%">.png</el-col>
  539. </el-form-item>
  540. </el-form>
  541. <div class="image">
  542. <div class="image-center">
  543. <img :src="imageUrl" class="image-url">
  544. </div>
  545. </div>
  546. </div>
  547. </normal-dialog>
  548. </el-container>
  549. <el-button id="pasteUpload" type="primary" style="display: none" @click.stop="pasteUpload">upload</el-button>
  550. </div>
  551. </template>
  552. <script>
  553. const _ = require('lodash')
  554. import ElImageViewer from 'element-ui/packages/image/src/image-viewer'
  555. import { EncryptId, analysisBizId_id } from '@/utils/crypto-js.js'
  556. import statusChange from '@/views/projectManage/bugList/details/statusChange'
  557. import { getCommentList, addComment } from '@/api/requirement.js'
  558. import {
  559. bugDetails,
  560. bugGetEnum,
  561. bugDelete,
  562. bugUpdate,
  563. taskListCreate,
  564. releaseList,
  565. settingQueryBizModuleList,
  566. operationLogBug
  567. } from '@/api/defectManage.js'
  568. import normalDialog from '@/components/dialog/normalDialog'
  569. import createdBug from '@/views/projectManage/bugList/file/createdBug'
  570. import axios from 'axios'
  571. import { deepClone } from '@/utils/global'
  572. import searchPeople from '@/components/select/searchPeople' // 人员select
  573. // import tinymce from 'tinymce/tinymce'
  574. // import Editor from '@tinymce/tinymce-vue'
  575. import normalArea from '@/components/input/normalArea' // 富文本
  576. import 'tinymce/plugins/table'// 插入表格插件
  577. // import 'tinymce/themes/silver/theme'
  578. // import 'tinymce/icons/default/icons'
  579. import Tag from '@/components/Tag'
  580. document.body.onpaste = function(event) {
  581. const data = (event.clipboardData || window.clipboardData)
  582. const items = data.items
  583. const fileList = [] // 存储文件数据
  584. if (items && items.length) {
  585. // 检索剪切板items
  586. for (let i = 0; i < items.length; i++) {
  587. fileList.push(items[i].getAsFile())
  588. window.uploadFiles = fileList
  589. }
  590. document.getElementById('pasteUpload').click()
  591. }
  592. }
  593. export default {
  594. name: 'BugDetails',
  595. components: {
  596. createdBug,
  597. normalDialog,
  598. statusChange,
  599. searchPeople,
  600. ElImageViewer,
  601. normalArea,
  602. Tag
  603. },
  604. props: {
  605. id: {
  606. type: Number,
  607. default: -1
  608. },
  609. type: {
  610. type: String,
  611. default: 'page'
  612. },
  613. drawerShow: {
  614. type: Boolean,
  615. default: false
  616. }
  617. },
  618. data() {
  619. return {
  620. bugId: -1, // 当前缺陷Id
  621. bizId: -1, // 当前缺陷BizId
  622. srcList: [],
  623. // configure: {
  624. // language_url: '/tinymce/langs/zh_CN.js',
  625. // language: 'zh_CN',
  626. // skin_url: '/tinymce/skins/ui/oxide', // 编辑器需要一个skin才能正常工作,所以要设置一个skin_url指向之前复制出来的skin文件
  627. // browser_spellcheck: true, // 拼写检查
  628. // branding: false, // 去水印
  629. // elementpath: false, // 禁用编辑器底部的状态栏
  630. // statusbar: false, // 隐藏编辑器底部的状态栏
  631. // paste_data_images: true, // 允许粘贴图像
  632. // menubar: false, // 隐藏最上方menu
  633. // fontsize_formats: '14px 16px 18px 20px 24px 26px 28px 30px 32px 36px', // 字体大小
  634. // file_picker_types: 'image',
  635. // images_upload_credentials: true,
  636. // plugins: 'lists table textcolor wordcount contextmenu', // 引入插件
  637. // toolbar: 'bold italic underline strikethrough | fontsizeselect | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent table | undo redo | removeformat formatselect'
  638. // },
  639. text_content: '',
  640. clielIcon: true, // 所属任务交互
  641. active: false,
  642. changeRecord: [], // 变更记录Data
  643. modalShow: false,
  644. activeName: 'first', // 选项卡默认评论
  645. iconName: 'float_反馈_icon_close',
  646. bugNameIsFocus: false,
  647. props: { multiple: true },
  648. height: '700px',
  649. bugDescribeNoHtml: '',
  650. bugDescribe: '',
  651. bugNameForm: { bugName: '' },
  652. fileList: [],
  653. notImageList: [], // 文件非图片数组
  654. ImageList: [], // 文件图片数组
  655. accessory: null,
  656. dialogVisible: false,
  657. disabled: false,
  658. loading: {
  659. drawer: false,
  660. fullscreen: false,
  661. title: false,
  662. details: false,
  663. userInfo: false,
  664. time: false,
  665. appInfo: false,
  666. describe: false,
  667. accessory: false
  668. },
  669. userInformation: localStorage.getItem('username'),
  670. userNames: localStorage.getItem('realname'),
  671. comments: [],
  672. commentContent: '',
  673. uploadDialogVisible: false,
  674. uploadDialogImageUrl: '',
  675. num: 0,
  676. formHeight: '',
  677. formHeight1: '',
  678. statusDialogVisible: false,
  679. bug: {},
  680. bugModel: {},
  681. bugRemark: '',
  682. describeEditorVisible: false,
  683. enums: {},
  684. taskEnumList: [],
  685. appClientList: [],
  686. versionList: [],
  687. map: {},
  688. business_platform_Modular: [], // 业务/平台/模块
  689. bugNameTextareaStyle: {
  690. width: '1000px',
  691. position: 'relative'
  692. },
  693. bugNameTextareaWidth: 'auto',
  694. showWordLimit: false,
  695. showRule: false,
  696. rules: {
  697. assigner: [{ required: true, message: '责任人不可为空', trigger: 'blur' }],
  698. currentHandler: [{ required: true, message: '修复人不可为空', trigger: 'blur' }],
  699. bugName: [{ required: true, message: '标题不可为空', trigger: 'blur' }]
  700. },
  701. showCopyFile: false, // 复制文件对话框
  702. imageName: { name: null },
  703. imageUrl: null,
  704. bugData: {}, // bug数据
  705. uploadButton: this.drawerShow,
  706. imageRules: {
  707. name: [
  708. { required: true, message: '请输入图片名称', trigger: 'blur' },
  709. { min: 1, max: 50, message: '长度在 1 到 50 个字符', trigger: 'blur' }
  710. ]
  711. },
  712. statusObj: null, // 状态对象
  713. theBugTypeEnumList: [] // 缺陷类型
  714. }
  715. },
  716. watch: {
  717. id(newVal, oldVal) {
  718. this.bug = {}
  719. this.bugModel = {}
  720. this.bugNameForm = { bugName: '' }
  721. this.loading.drawer = false
  722. this.init()
  723. this.handleClicks()
  724. },
  725. drawerShow(newV, oldVal) {
  726. if (newV) {
  727. this.init()
  728. this.uploadButton = newV
  729. }
  730. },
  731. bizId: {
  732. handler(newV) {
  733. if (newV === -1) return
  734. this.taskListCreate() // 获取所属任务
  735. this.getBusinessLinePlatformModule()
  736. },
  737. immediate: true
  738. }
  739. },
  740. created() {
  741. this.analysisBizId_id()
  742. this.getBugSelect()
  743. if (this.type !== 'page') {
  744. var height = window.innerHeight > document.body.clientHeight ? window.innerHeight : document.body.clientHeight
  745. height -= 130
  746. this.height = height + 'px'
  747. }
  748. if (this.type === 'page' || this.type === 'drawer') {
  749. this.uploadButton = true
  750. }
  751. },
  752. mounted() {
  753. // tinymce.init({ selector: '.tinymce', height: '100' })
  754. this.bugGetEnum()
  755. const id = this.id === -1 ? this.bugId : this.id
  756. this.bugGet(id, false).then(res => {
  757. this.changeWidthOnBlur()
  758. this.getCommentList()
  759. releaseList().then(res => {
  760. this.appClientList = res.data.appClient // 客户端
  761. this.getVersionList(this.bug.appId)
  762. })
  763. this.RichText()
  764. })
  765. },
  766. methods: {
  767. analysisBizId_id() { // 解析路由中的bizId_id
  768. if (!this.$route.query.bizId_id) return
  769. const bizId_id = analysisBizId_id(this.$route.query.bizId_id)
  770. this.bugId = bizId_id[1]
  771. },
  772. debounceQuery: _.debounce(function() {
  773. this.taskListCreate(...arguments)
  774. }, 500),
  775. async taskListCreate(val) { // 获取所属任务
  776. const params = {
  777. bizId: this.bizId
  778. }
  779. if (val) {
  780. params.name = val
  781. } else {
  782. params.id = this.bug.taskId
  783. }
  784. console.log(params)
  785. const res = await taskListCreate(params)
  786. if (res.code === 200) {
  787. this.taskEnumList = res.data || []
  788. }
  789. },
  790. async getBugSelect() { // 获取下拉菜单option
  791. const res = await bugGetEnum()
  792. if (res.code === 200) {
  793. this.statusObj = {
  794. bugEnumList: res.data.bugEnumList, // status
  795. repairResultEnumList: res.data.repairResultEnumList, // 修复结果
  796. bugReasonEnumList: res.data.bugReasonEnumList, // 缺陷原因
  797. theBugTypeEnumList: this.deleteChild(res.data.theBugTypeEnumList) // 缺陷类型
  798. }
  799. }
  800. },
  801. deleteChild(arr) { // 删除无用子属性
  802. const bfs = arr => {
  803. arr.forEach(item => {
  804. if (!item.childrenEnums || item.childrenEnums.length === 0) {
  805. delete item.childrenEnums
  806. } else {
  807. this.deleteChild(item.childrenEnums)
  808. }
  809. })
  810. }
  811. bfs(arr)
  812. return arr
  813. },
  814. handleClick(tab) {
  815. if (tab.label === '变更记录') {
  816. operationLogBug(this.bug.id).then(res => {
  817. this.changeRecord = res.data
  818. })
  819. }
  820. },
  821. handleClicks() {
  822. operationLogBug(this.id).then(res => {
  823. this.changeRecord = res.data
  824. })
  825. },
  826. closeDrawer() {
  827. this.$emit('close', false)
  828. },
  829. init() {
  830. this.getBugSelect()
  831. this.formHeight = ''
  832. this.formHeight1 = ''
  833. this.bugGet(this.id, false).then(res => {
  834. this.changeWidthOnBlur()
  835. this.getCommentList()
  836. releaseList().then(res => {
  837. this.appClientList = res.data.appClient // 客户端
  838. this.getVersionList(this.bug.appId)
  839. })
  840. this.text_content = this.bugDescribe === null ? '' : this.bugDescribe
  841. })
  842. },
  843. listen(event) {
  844. event.preventDefault() // 阻止浏览器默认换行操作
  845. return false
  846. },
  847. JumpRequire(id) {
  848. const bizId_id = EncryptId(`${this.bizId}_${id}`)
  849. this.$router.push({ name: '需求详情', query: { bizId_id: bizId_id }})
  850. },
  851. getToDetails() {
  852. const bizId_id = EncryptId(`${this.bizId}_${this.id}`)
  853. this.$router.push({ name: '缺陷详情', query: { bizId_id: bizId_id }})
  854. },
  855. isImage(arr) {
  856. const reg = new RegExp(/.*(\.gif|\.jpeg|\.png|\.jpg|\.bmp)/i)
  857. return arr.filter(item => { return !!item.url.match(reg) })
  858. },
  859. isNotImage(arr) {
  860. const reg = new RegExp(/.*(\.gif|\.jpeg|\.png|\.jpg|\.bmp)/i)
  861. return arr.filter(item => { return !item.url.match(reg) })
  862. },
  863. beforeUpload(file) {
  864. const reg = new RegExp(/.*(zip|xlsx|text|csv|xls|mov|mp4|m4a|avi|amr|mp3|wav|3gpp|gif|jpeg|png|jpg)/i)
  865. const isUpload = file.type.match(reg)
  866. const isName = file.name.match(reg)
  867. if (isUpload || isName) {
  868. const isLt200M = (file.size / 1024 / 1024) < 200
  869. if (!isLt200M) {
  870. this.$message({
  871. message: '上传文件大小不能超过 200MB!',
  872. type: 'warning'
  873. })
  874. return false
  875. }
  876. } else {
  877. this.$message({
  878. message: '不支持上传此文件格式',
  879. type: 'warning'
  880. })
  881. return false
  882. }
  883. },
  884. beforeRemove(file, fileList) {
  885. let a = true
  886. if (file && file.status === 'success') {
  887. a = this.$confirm(`确定删除 ${file.name}吗?`)
  888. }
  889. return a
  890. },
  891. handleRemoveNotImage(file, fileList) {
  892. this.notImageList = this.notImageList.filter(item => {
  893. return item.url !== file.url
  894. })
  895. this.bug.accessory = JSON.stringify([...this.notImageList, ...this.ImageList])
  896. this.bugUpdate(this.bug, 'accessory')
  897. },
  898. handleRemoveImage(file, fileList) {
  899. this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
  900. confirmButtonText: '确定',
  901. cancelButtonText: '取消',
  902. type: 'warning'
  903. }).then(() => {
  904. this.ImageList = this.ImageList.filter(item => {
  905. return item.url !== file.url
  906. })
  907. this.bug.accessory = JSON.stringify([...this.notImageList, ...this.ImageList])
  908. this.bugUpdate(this.bug, 'accessory')
  909. }).catch(() => {
  910. this.$message({
  911. type: 'info',
  912. message: '已取消删除'
  913. })
  914. })
  915. },
  916. closeViewer() {
  917. this.dialogVisible = false
  918. this.srcList = []
  919. },
  920. handlePictureCardPreview(file) {
  921. const index = this.ImageList.findIndex(item => item.url === file.url)
  922. const thobj = this.ImageList.splice(index, 1)
  923. this.ImageList.splice(0, 0, ...thobj)
  924. this.ImageList.map(item => {
  925. this.srcList.push(item.url)
  926. })
  927. this.dialogVisible = true
  928. },
  929. handleChange(response, file, fileList) {
  930. const reg = new RegExp(/.*(\.gif|\.jpeg|\.png|\.jpg|\.bmp|\.gif)/i)
  931. if (!response.success) {
  932. this.$message({
  933. showClose: true,
  934. message: response.error,
  935. type: 'error'
  936. })
  937. this.notImageList.pop()
  938. return false
  939. }
  940. const item = {
  941. name: file.name,
  942. url: 'http:' + file.response.url
  943. }
  944. const arr1 = deepClone(this.ImageList)
  945. const arr2 = deepClone(this.notImageList)
  946. item.url.match(reg) ? arr1.push(item) : arr2.push(item)
  947. this.bug.accessory = JSON.stringify([...arr2, ...arr1])
  948. this.$message({
  949. showClose: true,
  950. message: '文件上传成功',
  951. type: 'success'
  952. })
  953. this.bugUpdate(this.bug, 'accessory')
  954. },
  955. errorChange() {
  956. this.$message({
  957. showClose: true,
  958. message: '上传失败',
  959. type: 'error'
  960. })
  961. },
  962. handleDownload(file) { // 下载图片
  963. const xhr = new XMLHttpRequest()
  964. xhr.open('get', file.url, true)
  965. xhr.responseType = 'blob'
  966. xhr.onload = () => {
  967. if (xhr.status === 200) {
  968. this.saveAs(xhr.response, file.name)
  969. }
  970. }
  971. xhr.send()
  972. },
  973. saveAs(data, name) { // 保存图片
  974. const urlObject = window.URL || window.webkitURL || window
  975. const file = new Blob([data])
  976. const a = document.createElement('a')
  977. a.href = urlObject.createObjectURL(file)
  978. a.download = name
  979. a.click()
  980. },
  981. getCommentList() {
  982. getCommentList({ type: 2, joinId: this.bug.id }).then(res => {
  983. this.comments = res.data
  984. this.commentContent = ''
  985. })
  986. },
  987. addComment() {
  988. if (!this.commentContent) {
  989. this.$message.warning('评论不能为空')
  990. return
  991. }
  992. addComment({ commentInfo: { joinId: this.bug.id, type: 2, content: this.commentContent }, user: { ename: this.userInformation }}).then(res => {
  993. if (res.code === 200) {
  994. this.getCommentList()
  995. }
  996. })
  997. },
  998. changeRule() {
  999. if (this.bugNameForm.bugName) {
  1000. this.rules.bugName[0].trigger = 'blur'
  1001. this.showRule = false
  1002. } else {
  1003. this.rules.bugName[0].trigger = 'change'
  1004. this.showRule = true
  1005. }
  1006. },
  1007. changeWidthOnBlur() {
  1008. if (this.type !== 'page') {
  1009. return
  1010. }
  1011. const colLength = document.getElementById('divLength').offsetWidth
  1012. let bugNameLength = document.getElementById('spanLength').offsetWidth
  1013. const statusButtonLength = document.getElementById('itemLength1').offsetWidth
  1014. const deleteButtonLength = document.getElementById('itemLength2').offsetWidth
  1015. const labelLength = document.getElementsByClassName('el-form-item__label')[0].offsetWidth
  1016. const wholeLength = colLength - statusButtonLength - labelLength - deleteButtonLength - 20
  1017. if (bugNameLength + 40 < wholeLength) {
  1018. bugNameLength += 40
  1019. this.bugNameTextareaStyle.width = bugNameLength + 'px'
  1020. } else {
  1021. this.bugNameTextareaStyle.width = wholeLength + 'px'
  1022. }
  1023. },
  1024. changeWidthOnFocus() {
  1025. const colLength = document.getElementById('divLength').offsetWidth
  1026. const statusButtonLength = document.getElementById('itemLength1').offsetWidth
  1027. const deleteButtonLength = document.getElementById('itemLength2').offsetWidth
  1028. const labelLength = document.getElementsByClassName('el-form-item__label')[0].offsetWidth
  1029. const wholeLength = colLength - statusButtonLength - labelLength - deleteButtonLength - 20
  1030. this.bugNameTextareaStyle.width = wholeLength + 'px'
  1031. },
  1032. changeBugName() {
  1033. if (this.bugNameForm.bugName && this.bugNameForm.bugName.length < 1) {
  1034. this.bugNameForm.bugName = this.bug.bugName
  1035. return
  1036. }
  1037. this.bug.bugName = this.bugNameForm.bugName
  1038. this.bugUpdate(this.bug, 'title')
  1039. },
  1040. RichText() {
  1041. this.text_content = this.bugDescribe === null ? '' : this.bugDescribe
  1042. },
  1043. describeCancel() {
  1044. this.text_content = this.bug.bugDescribe
  1045. this.describeEditorVisible = false
  1046. },
  1047. describeConfirm() {
  1048. this.bug.bugDescribe = this.text_content
  1049. // if (this.text_content) {
  1050. // this.bugDescribeNoHtml = this.text_content
  1051. // .replace(/<[^>]+>/g, '')
  1052. // .replace(/&nbsp;/gi, '')
  1053. // }
  1054. this.bugUpdate(this.bug, 'describe').then(res => {
  1055. if (res.code === 200) {
  1056. this.describeEditorVisible = false
  1057. this.bugGet(this.bug.id)
  1058. this.$emit('update', false)
  1059. }
  1060. })
  1061. },
  1062. getVersionList(e) {
  1063. // 获取版本号
  1064. const list = this.appClientList.filter(value => value.code === e)
  1065. this.versionList = list[0] ? list[0].childEnumInfos : []
  1066. },
  1067. updateBugStatus() {
  1068. this.bugGet(this.bug.id)
  1069. this.$emit('update', false)
  1070. },
  1071. bugGet(id, isLoading) {
  1072. this.loading.fullscreen = isLoading
  1073. return bugDetails({ id: id }).then(res => {
  1074. if (res.code === 200) {
  1075. if (this.num === 0) {
  1076. document.getElementsByClassName('scop')[0].scrollTop = 0
  1077. }
  1078. this.bug = res.data
  1079. this.bug.tags = res.data.tags || []
  1080. this.bizId = this.bug.bizId
  1081. this.bug.currentHandler = res.data.currentHandler.split(',')
  1082. this.bug.assigner = res.data.assigner.split(',')
  1083. this.bugModel = JSON.parse(JSON.stringify(res.data))
  1084. this.bugDescribe = this.bug.bugDescribe
  1085. // if (this.bugDescribe !== null) {
  1086. // this.bugDescribeNoHtml = this.bugDescribe
  1087. // .replace(/<[^>]+>/g, '')
  1088. // .replace(/&nbsp;/gi, '')
  1089. // }
  1090. // console.log(this.bugDescribe)
  1091. this.bugNameForm.bugName = this.bug.bugName
  1092. this.bug.networkType = this.bug.networkType
  1093. this.bug.appVersion = this.bug.appVersion
  1094. this.bug.moduleIds = this.bug.moduleIds
  1095. this.fileList = []
  1096. var str = res.data.accessory
  1097. if (str !== '' && str !== null) {
  1098. var obj = JSON.parse(str.split('{}')[0])
  1099. for (var a of obj) {
  1100. this.fileList.push(a)
  1101. }
  1102. }
  1103. this.ImageList = this.isImage(this.fileList)
  1104. this.notImageList = this.isNotImage(this.fileList)
  1105. } else {
  1106. this.loading.drawer = true
  1107. return Promise.reject(res.msg)
  1108. }
  1109. this.loading.fullscreen = false
  1110. return res
  1111. })
  1112. },
  1113. openQueryDialog() {
  1114. this.modalShow = true
  1115. this.$nextTick(() => {
  1116. this.$refs.createdBug.init(2, this.bug)
  1117. })
  1118. },
  1119. father() {
  1120. this.bugGet(this.bug.id, false)
  1121. this.$emit('update', false)
  1122. },
  1123. bugGetEnum() {
  1124. return bugGetEnum().then(res => {
  1125. this.enums = res.data
  1126. if (this.enums) {
  1127. for (const i in this.enums) {
  1128. this.map[i] = {}
  1129. if (this.enums[i]) {
  1130. for (const j in this.enums[i]) {
  1131. this.map[i][this.enums[i][j].code] = this.enums[i][j].name
  1132. }
  1133. }
  1134. }
  1135. }
  1136. this.theBugTypeEnumList = this.deleteChild(res.data.theBugTypeEnumList) // 缺陷类型
  1137. return res
  1138. })
  1139. },
  1140. bugUpdate(form, loadingStr) {
  1141. if (form.currentHandler && form.currentHandler.length > 0) {
  1142. if (form.assigner && form.assigner.length > 0) {
  1143. this.formHeight = ''
  1144. this.formHeight1 = ''
  1145. loadingStr ? this.loading[loadingStr] = true : this.loading.fullscreen = true
  1146. const userData = { id: '', ename: this.userInformation, name: this.userNames }
  1147. this.num = 1
  1148. let objData
  1149. const data = _.cloneDeep(form)
  1150. if (form) {
  1151. data.currentHandler = form.currentHandler.join(',')
  1152. data.assigner = form.assigner.join(',')
  1153. objData = { bugBaseInfo: data, user: userData }
  1154. } else {
  1155. data.currentHandler = form.currentHandler.join(',')
  1156. data.assigner = form.assigner.join(',')
  1157. objData = { bugBaseInfo: data, user: userData }
  1158. }
  1159. return bugUpdate(objData).then(res => {
  1160. if (res.code === 200) {
  1161. this.changeWidthOnBlur()
  1162. this.clielIcon = true
  1163. this.active = false
  1164. this.bugModel = JSON.parse(JSON.stringify(this.bug))
  1165. this.$emit('update', false)
  1166. this.init()
  1167. } else {
  1168. this.bug = JSON.parse(JSON.stringify(this.bugModel))
  1169. }
  1170. loadingStr ? this.loading[loadingStr] = false : this.loading.fullscreen = false
  1171. return res
  1172. })
  1173. } else {
  1174. this.formHeight1 = 'type'
  1175. }
  1176. } else {
  1177. this.formHeight = 'type'
  1178. }
  1179. },
  1180. async bugUpdates(form, loadingStr) {
  1181. if (form.currentHandler.length > 0) {
  1182. if (form.assigner.length > 0) {
  1183. this.formHeight = ''
  1184. this.formHeight1 = ''
  1185. loadingStr ? this.loading[loadingStr] = true : this.loading.fullscreen = true
  1186. const userData = { id: '', ename: this.userInformation, name: this.userNames }
  1187. this.num = 1
  1188. const data = _.cloneDeep(form)
  1189. data.currentHandler = form.currentHandler.join(',')
  1190. data.assigner = form.assigner.join(',')
  1191. const objData = { bugBaseInfo: data, user: userData }
  1192. const res = await bugUpdate(objData)
  1193. if (res.code === 200) {
  1194. this.changeWidthOnBlur()
  1195. this.clielIcon = true
  1196. this.active = false
  1197. this.bugModel = JSON.parse(JSON.stringify(this.bug))
  1198. this.$emit('update', false)
  1199. this.init()
  1200. } else {
  1201. this.bug = JSON.parse(JSON.stringify(this.bugModel))
  1202. }
  1203. loadingStr ? this.loading[loadingStr] = false : this.loading.fullscreen = false
  1204. } else {
  1205. this.formHeight1 = 'type'
  1206. }
  1207. } else {
  1208. this.formHeight = 'type'
  1209. }
  1210. },
  1211. getBusinessLinePlatformModule() {
  1212. settingQueryBizModuleList(this.bizId).then(res => {
  1213. this.business_platform_Modular = res.data.map(item => ({
  1214. ...item,
  1215. value: item.id,
  1216. label: item.moduleName,
  1217. children: item.childModules.length === 0 ? null : item.childModules.map(item1 => ({
  1218. ...item1,
  1219. value: item1.id,
  1220. label: item1.moduleName,
  1221. children: item1.childModules.length === 0 ? null : item1.childModules.map(item2 => ({
  1222. ...item2,
  1223. value: item2.id,
  1224. label: item2.moduleName
  1225. }))
  1226. }))
  1227. }))
  1228. })
  1229. },
  1230. openDeleteDialog() {
  1231. // this.statusDialogForm = JSON.parse(JSON.stringify(this.bug))
  1232. // this.statusDialogForm.status = null
  1233. this.statusDialogVisible = true
  1234. },
  1235. JumpTask(id) { // 转到任务
  1236. const bizId_id = EncryptId(`${this.bizId}_${id}`)
  1237. this.$router.push({ name: '任务详情', query: { bizId_id: bizId_id }})
  1238. },
  1239. statusDialogConfirm() {
  1240. const userData = { id: '', ename: this.userInformation, name: this.userNames }
  1241. bugDelete(userData, this.bug.id).then(res => {
  1242. this.statusDialogVisible = false
  1243. if (res.code === 200) {
  1244. if (this.type === 'page') {
  1245. this.$router.push({ name: '缺陷' })
  1246. } else {
  1247. this.$emit('delete', false)
  1248. }
  1249. }
  1250. })
  1251. },
  1252. generateMixed(len) {
  1253. const chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
  1254. let res = ''
  1255. for (let i = 0; i < len; i++) {
  1256. const id = Math.ceil(Math.random() * 35)
  1257. res += chars[id]
  1258. }
  1259. return res
  1260. },
  1261. pasteUpload() {
  1262. if (!this.uploadButton) {
  1263. return false
  1264. }
  1265. if (window.uploadFiles[0]) {
  1266. const reader = new FileReader()
  1267. reader.readAsDataURL(window.uploadFiles[0])
  1268. reader.onload = () => {
  1269. const reg = new RegExp(/image\/png/)
  1270. this.imageUrl = reader.result
  1271. this.srcList.push(reader.result)
  1272. if (this.imageUrl.match(reg)) { // 判断是否是图片
  1273. this.showCopyFile = true
  1274. this.imageName.name = this.generateMixed(10)
  1275. }
  1276. }
  1277. }
  1278. },
  1279. mouseOver() {
  1280. this.active = true
  1281. },
  1282. // 移出
  1283. mouseLeave() {
  1284. this.active = false
  1285. },
  1286. getclielIcon() {
  1287. this.clielIcon = !this.clielIcon
  1288. this.$nextTick(() => {
  1289. this.$refs.searchOfChatRoom.focus()
  1290. })
  1291. },
  1292. mouseLeaves(e) {
  1293. e ? '' : this.clielIcon = true
  1294. },
  1295. async confirmUpload() {
  1296. if (this.imageName.name === null || this.imageName.name.replace(/\s+/g, '') === '') {
  1297. return false
  1298. }
  1299. // const isExist = this.ImageList.some(item => {
  1300. // return this.imageName.name === item.name
  1301. // })
  1302. // if (isExist) {
  1303. // this.$message({
  1304. // showClose: true,
  1305. // message: '图片名称重复',
  1306. // type: 'error'
  1307. // })
  1308. // return false
  1309. // }
  1310. this.showCopyFile = false
  1311. const res = await this.updateFile(window.uploadFiles[0])
  1312. const data = res.data
  1313. const item = {
  1314. name: `${this.imageName.name}.png` || `${this.generateMixed(10)}.png`,
  1315. status: 'success',
  1316. url: 'http:' + data.url
  1317. }
  1318. this.ImageList.push(item)
  1319. this.bug.accessory = JSON.stringify([...this.notImageList, ...this.ImageList])
  1320. this.$message({
  1321. showClose: true,
  1322. message: '文件上传成功',
  1323. type: 'success'
  1324. })
  1325. this.bugUpdate(this.bug, 'accessory')
  1326. this.imageName.name = null
  1327. this.imageUrl = null
  1328. window.uploadFiles = null
  1329. },
  1330. updateFile(file) {
  1331. const param = new FormData() // 创建form对象
  1332. param.append('file', file)// 通过append向form对象添加数据
  1333. const config = {
  1334. headers: {
  1335. 'Content-Type': 'multipart/form-data'
  1336. },
  1337. withCredentials: false
  1338. } // 添加请求头
  1339. return new Promise((resolve, reject) => {
  1340. axios.post('https://star.xiaojukeji.com/upload/img.node', param, config)
  1341. .then(response => {
  1342. resolve(response)
  1343. }).catch(err => {
  1344. reject(err)
  1345. })
  1346. })
  1347. }
  1348. }
  1349. }
  1350. </script>
  1351. <style scoped lang="less">
  1352. .timeForm {
  1353. width: 100%;
  1354. /deep/ .el-form-item {
  1355. display: inline-block;
  1356. width: 49%;
  1357. }
  1358. }
  1359. .layout_main {
  1360. border-radius: 4px;
  1361. }
  1362. </style>
  1363. <style lang="less">
  1364. .el-tooltip__popper.is-dark {
  1365. background-color: rgba(121,132,150, 0.8);
  1366. }
  1367. .bug_manage_container::-webkit-scrollbar {
  1368. display: none;
  1369. }
  1370. .bug_manage_title .button_delete{
  1371. font-size: 20px;
  1372. line-height: 14px;
  1373. padding: 7px 10px;
  1374. }
  1375. /* .bug_manage_title .button_delete2,.button_status .el-button {
  1376. padding: 7px 10px;
  1377. font-size: 14px;
  1378. font-weight: normal;
  1379. } */
  1380. .bug_manage_dialog .el-dialog {
  1381. border-radius:4px;
  1382. }
  1383. .bug_manage_dialog .el-dialog__header {
  1384. padding: 20px 45px;
  1385. }
  1386. .bug_manage_dialog .el-dialog__body {
  1387. padding: 0px 45px;
  1388. }
  1389. .bug_manage_dialog .el-dialog__footer {
  1390. padding: 20px 45px 40px 0;
  1391. }
  1392. .layout_header,
  1393. .layout_aside,
  1394. .layout_main {
  1395. background-color: #ffffff;
  1396. }
  1397. .bug_manage_dialog_fixMethod .el-form-item__label {
  1398. padding-left: 10px;
  1399. }
  1400. .bug_manage_dialog .el-form-item__label {
  1401. color: #666666;
  1402. font-weight: 400;
  1403. }
  1404. .bug_manage_title .el-form-item__label {
  1405. color: #333b4a;
  1406. font-size: 18px;
  1407. font-weight: 500;
  1408. }
  1409. #spanLength {
  1410. color: #333b4a;
  1411. font-size: 20px;
  1412. font-weight: 500;
  1413. visibility: hidden;
  1414. position: absolute;
  1415. }
  1416. #divLength,
  1417. #divLength2 {
  1418. padding-top: 30px;
  1419. }
  1420. .bug_manage_title .el-textarea__inner {
  1421. border: transparent;
  1422. resize: none;
  1423. color: #333b4a;
  1424. font-size: 20px;
  1425. font-weight: 500;
  1426. overflow:hidden;
  1427. }
  1428. .bug_manage_title .el-textarea__inner:focus {
  1429. padding-right: 50px;
  1430. }
  1431. .bug_manage_title .el-input__count {
  1432. background-color: transparent;
  1433. }
  1434. .bug_manage_title .bug_manage_bug_name_rule_show .el-textarea__inner{
  1435. border: 1px solid #F56C6C;
  1436. }
  1437. .bug_manage_title .bug_manage_bug_name_focus .el-textarea__inner:focus {
  1438. border: 1px solid #409eff;
  1439. }
  1440. .bug_manage_title .bug_manage_bug_name .el-textarea__inner:hover {
  1441. border: thin solid #DCDFE6;
  1442. }
  1443. .bug_manage .el-form-item__label {
  1444. color: #666666;
  1445. font-weight: 400;
  1446. padding: 0;
  1447. }
  1448. .bug_manage .el-input__inner {
  1449. border: transparent;
  1450. color: #666666;
  1451. font-weight: 500;
  1452. }
  1453. .bug_manage .bug_manage_div {
  1454. border: transparent;
  1455. color: #666666;
  1456. font-weight: 500;
  1457. padding-left: 13px
  1458. }
  1459. .bug_manage .el-cascader .el-input__inner:hover {
  1460. border: thin solid #DCDFE6;
  1461. }
  1462. .bug_manage .el-cascader .el-input__inner:focus {
  1463. border: 1px solid #409eff;
  1464. }
  1465. .bug_manage .el-input__inner:hover {
  1466. border: thin solid #DCDFE6;
  1467. }
  1468. .bug_manage .el-input__inner:focus {
  1469. border: 1px solid #409eff;
  1470. }
  1471. .bug_manage .el-select,.el-cascader {
  1472. width: 100%;
  1473. }
  1474. .bug_manage .el-select .el-input__suffix-inner {
  1475. visibility: hidden;
  1476. }
  1477. .bug_manage .el-select:hover .el-input__suffix-inner {
  1478. visibility: visible;
  1479. }
  1480. .bug_manage .el-cascader .el-input__suffix-inner {
  1481. visibility: hidden;
  1482. }
  1483. .bug_manage .el-cascader:hover .el-input__suffix-inner {
  1484. visibility: visible;
  1485. }
  1486. .layout_main .el-form-item.is-error .el-input__inner {
  1487. border-color: #DCDFE6;
  1488. }
  1489. </style>
  1490. <style scoped>
  1491. .el-aside {
  1492. overflow: hidden;
  1493. }
  1494. .bug_describe {
  1495. color: #666666;
  1496. font-size: 14px;
  1497. }
  1498. .bug_describe:hover {
  1499. color: #409eff;
  1500. cursor: pointer;
  1501. }
  1502. .bug_describe_content {
  1503. border-radius: 4px;
  1504. border: 1px solid transparent;
  1505. }
  1506. .bug_describe_content:hover {
  1507. border-radius: 4px;
  1508. border: 1px solid rgba(191, 198, 220);
  1509. }
  1510. </style>
  1511. <style scoped lang="less">
  1512. .limit-font {
  1513. /deep/.el-form-item__content {
  1514. span {
  1515. display: inline-block;
  1516. line-height: 20px;
  1517. }
  1518. }
  1519. }
  1520. .str-task-name {
  1521. margin-left: 14px;
  1522. color: #666666;
  1523. font-weight: 500;
  1524. }
  1525. .str-task-name:hover {
  1526. color:#409EFF;
  1527. cursor: pointer;
  1528. }
  1529. /deep/ .el-tabs__nav-wrap::after {
  1530. content: "";
  1531. position: absolute;
  1532. left: 0;
  1533. bottom: 0;
  1534. width: 100%;
  1535. height: 2px;
  1536. background-color: #FFF;
  1537. z-index: 1;
  1538. }
  1539. .operatorName {
  1540. min-width: 50px;
  1541. font-size:14px;
  1542. font-family:PingFang SC;
  1543. line-height:20px;
  1544. color:rgba(51,59,74,1);
  1545. opacity:1;
  1546. margin-right: 10px;
  1547. }
  1548. .createTime {
  1549. min-width:150px;
  1550. font-size:14px;
  1551. font-family:PingFangSC-Regular;
  1552. line-height:20px;
  1553. color:rgba(68,68,68,1);
  1554. opacity:1;
  1555. }
  1556. .remark {
  1557. width:448px;
  1558. font-size:14px;
  1559. font-family:PingFangSC-Regular;
  1560. line-height:20px;
  1561. color:#444444;
  1562. opacity:1;
  1563. }
  1564. .bug_removeDate {
  1565. height:16px;
  1566. background:rgba(64,158,255,0.1);
  1567. border:1px solid #409EFF;
  1568. opacity:1;
  1569. border-radius:2px;
  1570. font-size:8px;
  1571. font-family:MicrosoftYaHei;
  1572. line-height:14px !important;
  1573. color:#409EFF;
  1574. }
  1575. .bug_date {
  1576. padding: 0 3px;
  1577. background:rgba(245,108,108,0.1);
  1578. border:1px solid rgba(245,108,108,1);
  1579. opacity:1;
  1580. border-radius:2px;
  1581. font-size:8px;
  1582. font-family:MicrosoftYaHei;
  1583. line-height:10px;
  1584. color:rgba(245,108,108,1);
  1585. }
  1586. .newBtn {
  1587. font-size: 14px;
  1588. color: rgb(51, 59, 74);
  1589. font-weight: 400;
  1590. }
  1591. .newBtn:hover {
  1592. color: #409EFF;
  1593. cursor: pointer;
  1594. }
  1595. // .current:hover{
  1596. // cursor: pointer;
  1597. // border-radius: 4px;
  1598. // border: 1px solid #DCDFE6;
  1599. // }
  1600. .module_title{
  1601. display:flex;
  1602. align-items: center;
  1603. margin-bottom: 20px
  1604. }
  1605. .module_title__sign {
  1606. width:4px;
  1607. height:15px;
  1608. background:#409EFF;
  1609. border-radius:1px;
  1610. }
  1611. .module_title__caption{
  1612. width:83px;
  1613. font-size:16px;
  1614. font-family:MicrosoftYaHei;
  1615. color:rgba(51,59,74,1);
  1616. margin-left:6px;
  1617. font-weight: 500;
  1618. }
  1619. .layout_main .el-form-item{
  1620. margin-bottom:0px;
  1621. }
  1622. .layout_main .el-divider--horizontal{
  1623. margin:10px 0 5px 0;
  1624. }
  1625. .tips {
  1626. color:#DCDFE6;
  1627. position: absolute;
  1628. }
  1629. .file-dialog {
  1630. display: flex;
  1631. flex-direction: column;
  1632. align-items: center;
  1633. .el-form {
  1634. width: 100%;
  1635. }
  1636. .image {
  1637. position: relative;
  1638. width: 61%;
  1639. padding-top: 60%;
  1640. border:1px solid #409EFF;
  1641. border-radius: 4px;
  1642. .image-center {
  1643. padding: 1%;
  1644. position: absolute;
  1645. top: 0;
  1646. left: 0;
  1647. width: 100%;
  1648. height: 100%;
  1649. overflow-x: hidden;
  1650. display: flex;
  1651. justify-content: center;
  1652. }
  1653. .image-url {
  1654. height: 90vh;
  1655. }
  1656. }
  1657. }
  1658. .upload-button {
  1659. position: relative;
  1660. color:#409EFF;
  1661. border: 1px dashed #409EFF;
  1662. background-color: #F7FBFF;
  1663. }
  1664. .upload-main {
  1665. position: relative;
  1666. }
  1667. .upload-info {
  1668. position: absolute;
  1669. left: 130px;
  1670. top: 14px;
  1671. color:#409EFF;
  1672. font-size: 12px;
  1673. i{
  1674. margin-right: 5px;
  1675. }
  1676. }
  1677. .upload-file {
  1678. color: #E6A23C;
  1679. font-size: 12px;
  1680. i{
  1681. margin-right: 5px;
  1682. }
  1683. }
  1684. .upload-title {
  1685. width: 40px;
  1686. }
  1687. .bug-detail /deep/ .el-upload-dragger {
  1688. width: 248px;
  1689. height: 148px;
  1690. }
  1691. .bug-detail /deep/ .el-upload--picture-card {
  1692. border: none;
  1693. }
  1694. /deep/ .el-upload--picture-card {
  1695. display: none;
  1696. }
  1697. /deep/.el-upload-list--picture-card .el-upload-list__item {
  1698. height: 168px;
  1699. }
  1700. .el-upload-list__item-actions {
  1701. height: calc(100%-20px);
  1702. margin-top: 20px;
  1703. }
  1704. .image-detail {
  1705. height: 100%;
  1706. position: relative
  1707. }
  1708. .detail-img {
  1709. padding-top: 20px;
  1710. }
  1711. .image-name {
  1712. box-sizing: border-box;
  1713. position: absolute;
  1714. top: 0;
  1715. width: 100%;
  1716. height: 20px;
  1717. display: flex;
  1718. justify-content: center;
  1719. font-size: 12px;
  1720. border-bottom: 1px solid #c0ccda;
  1721. overflow: hidden;
  1722. text-overflow: ellipsis;
  1723. white-space: nowrap;
  1724. }
  1725. .belong-task {
  1726. max-width: 500px;
  1727. display: flex;
  1728. .modules-name {
  1729. width: calc(100% - 100px);
  1730. overflow: hidden;
  1731. text-overflow: ellipsis;
  1732. white-space: nowrap;
  1733. }
  1734. .modules {
  1735. color: #999999;
  1736. }
  1737. .task-id {
  1738. color: #999999;
  1739. width: 80px;
  1740. margin-right: 20px;
  1741. }
  1742. .name {
  1743. color: #333333;
  1744. margin-right: 20px;
  1745. }
  1746. }
  1747. </style>