serviceImprovementRule.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. <template>
  2. <div>
  3. <el-button size="small" style="width:120px" type="primary" @click="openDialog">{{ $t('设 置') }}</el-button>
  4. <el-dialog :title="$t(`服务改善自动创建规则`)" append-to-body :visible.sync="dialogVisible" width="60%">
  5. <div style="margin-bottom: 16px;">
  6. <div style="display: flex; justify-content: space-between; align-items: center;">
  7. <span style="font-weight: bold;">{{ $t('设置真因分析产品经理') }}</span>
  8. <el-button type="primary" size="mini" @click="addRow">{{ $t('添加行') }}</el-button>
  9. </div>
  10. </div>
  11. <el-table :data="tableData" style="width: 100%;height: calc(100vh - 500px);overflow: auto" border size="small">
  12. <el-table-column :label="$t('产品经理')" prop="name" width="100px">
  13. <template slot-scope="scope" >
  14. <el-popover
  15. v-if="scope.row.timestamp"
  16. placement="bottom-start"
  17. width="600"
  18. v-model="scope.row.popoverVisible"
  19. trigger="click"
  20. @show="resetParam"
  21. >
  22. <el-input
  23. v-model="managerParam.content.where.condition"
  24. :placeholder="$t('搜索')"
  25. size="small"
  26. class="mb-10"
  27. @keyup.native.enter="searchManager"
  28. @clear="searchManager"
  29. clearable
  30. >
  31. <i slot="suffix" class="el-input__icon el-icon-search" @click="searchManager"></i>
  32. </el-input>
  33. <tableTemplate
  34. v-if="scope.row.popoverVisible"
  35. ref="managerTable"
  36. :layout="managerTableCols"
  37. :param="managerParam"
  38. :isInput="false"
  39. :isPagination="true"
  40. :height="'300px'"
  41. size="mini"
  42. @rowClick="(val) => handleManagerSelect(val, scope.row)"
  43. />
  44. <el-input
  45. slot="reference"
  46. v-model="scope.row.name"
  47. :placeholder="$t('请选择')"
  48. readonly
  49. style="width: 100%"
  50. size="mini"
  51. />
  52. </el-popover>
  53. <span v-else>{{ scope.row.name }}</span>
  54. </template>
  55. </el-table-column>
  56. <el-table-column :label="$t('商品型号')" width="600px">
  57. <template slot-scope="scope">
  58. <el-popover
  59. v-if="scope.row.timestamp"
  60. placement="bottom-start"
  61. width="600"
  62. v-model="scope.row.modelPopoverVisible"
  63. trigger="click"
  64. @show="resetModelParam(scope.row, scope.$index)"
  65. >
  66. <el-input
  67. v-model="modelParam.content.where.condition"
  68. :placeholder="$t('搜索')"
  69. size="small"
  70. class="mb-10"
  71. @keyup.native.enter="searchModel(scope.row, scope.$index)"
  72. @clear="searchModel(scope.row, scope.$index)"
  73. clearable
  74. >
  75. <i slot="suffix" class="el-input__icon el-icon-search" @click="searchModel(scope.row, scope.$index)"></i>
  76. </el-input>
  77. <tableTemplate
  78. v-if="scope.row.modelPopoverVisible"
  79. ref="modelTable"
  80. :layout="modelTableCols"
  81. :param="modelParam"
  82. :isInput="false"
  83. :isPagination="true"
  84. :height="'300px'"
  85. size="mini"
  86. :checkbox="true"
  87. :rowKey="getModelValue"
  88. :reserveSelection="true"
  89. @selectionChange="(val) => handleModelSelectionChange(val, scope.row)"
  90. @listData="(val) => syncModelSelection(val, scope.row, scope.$index)"
  91. />
  92. <el-input
  93. slot="reference"
  94. :value="formatModelLabel(scope.row.productModel)"
  95. :placeholder="$t('请选择')"
  96. readonly
  97. @click.native="modelParam.content.hrid = scope.row.hrid"
  98. style="width: 100%"
  99. size="mini"
  100. />
  101. </el-popover>
  102. <span v-else>{{ formatModelLabel(scope.row.productModel) || $t('未选择') }}</span>
  103. </template>
  104. </el-table-column>
  105. <el-table-column :label="$t('客诉大类及其发生次数')">
  106. <template slot-scope="scope">
  107. <div v-if="scope.row.class2s && scope.row.class2s.length > 0">
  108. <span v-for="(item,index) in scope.row.class2s" :key="item.index">
  109. <span v-if="scope.row.class2s.length -1 == index">
  110. {{item.str}}
  111. </span>
  112. <span v-else>{{item.str + ','}}</span>
  113. </span>
  114. </div>
  115. <div v-else>--</div>
  116. </template>
  117. </el-table-column>
  118. <el-table-column :label="$t('操作')" width="130px" align="center">
  119. <template slot-scope="scope">
  120. <el-button v-if="scope.row.timestamp" type="text" size="mini" @click="saveRow(scope.row)" class="inline-16">{{ $t('保存') }}</el-button>
  121. <el-button type="text" size="mini" @click="deleteRow(scope.row, scope.$index)" class="inline-16">{{ $t('删除') }}</el-button>
  122. <!-- <el-button type="text" size="mini" v-if="!scope.row.timestamp" class="inline-16" @click="setNumber">{{$t(`设置次数`)}}</el-button>-->
  123. <setServiceSet v-if="!scope.row.timestamp" class="inline-16" :data="scope.row" @setSuccess="setSuccess"></setServiceSet>
  124. </template>
  125. </el-table-column>
  126. </el-table>
  127. <div class="dialog-footer" style="margin-top: 20px; text-align: right;">
  128. <el-button size="small" @click="dialogVisible = false" class="normal-btn-width">{{$t('取 消')}}</el-button>
  129. <!-- <el-button size="small" type="primary" class="normal-btn-width" @click="dialogVisible = false">{{$t('确 定')}}</el-button>-->
  130. </div>
  131. </el-dialog>
  132. </div>
  133. </template>
  134. <script>
  135. import tableTemplate from '@/template/popoverTable/table'
  136. import setServiceSet from './setServiceSet'
  137. export default {
  138. name: "serviceImprovementRule",
  139. components: { tableTemplate , setServiceSet },
  140. data() {
  141. return {
  142. dialogVisible: false,
  143. tableData: [],
  144. managerOptions: [], // 应该从接口获取
  145. managerTableCols: [
  146. { columnname: 'name', title: this.$t('姓名') },
  147. { columnname: 'depname', title: this.$t('部门') },
  148. { columnname: 'position', title: this.$t('职位') },
  149. { columnname: 'depname', title: this.$t('负责区域') },
  150. { columnname: 'phonenumber', title: this.$t('手机号码') }
  151. ],
  152. modelTableCols: [
  153. { columnname: 'model', title: this.$t('型号') }
  154. ],
  155. managerParam: {
  156. "content": {
  157. "where": {
  158. "condition": ""
  159. },
  160. "pageNumber": 1,
  161. "pageSize": 20
  162. },
  163. "id": 2026011711061202,
  164. },
  165. modelParam: {
  166. "content": {
  167. "where": {
  168. "condition": ""
  169. },
  170. "pageNumber": 1,
  171. "pageSize": 20
  172. },
  173. "id": 2026011710020402,
  174. },
  175. }
  176. },
  177. methods: {
  178. openDialog() {
  179. this.dialogVisible = true;
  180. this.queryData();
  181. },
  182. addRow() {
  183. if (this.tableData.some(item => item.timestamp)) {
  184. this.$message.warning(this.$t('请先保存当前新增行'));
  185. return;
  186. }
  187. this.tableData.push({
  188. timestamp: new Date().getTime(),
  189. name: '',
  190. hrid: '',
  191. userid: '',
  192. productModel: [],
  193. popoverVisible: false,
  194. modelPopoverVisible: false
  195. });
  196. },
  197. async deleteRow(row,index) {
  198. console.log(row,index)
  199. if (row.timestamp) {
  200. this.tableData.splice(index, 1)
  201. } else {
  202. this.$confirm(this.$t('确认删除吗?'), this.$t('提示'), {
  203. confirmButtonText: this.$t('确定'),
  204. cancelButtonText: this.$t('取消'),
  205. type: 'warning'
  206. }).then(async () => {
  207. await this.$api.requested({
  208. "content": {
  209. "hrid": row.hrid
  210. },
  211. "id": 2026011710014002,
  212. });
  213. this.tableData.splice(index, 1);
  214. });
  215. }
  216. },
  217. resetParam() {
  218. this.managerParam.content.where.condition = '';
  219. this.managerParam.content.pageNumber = 1;
  220. this.$nextTick(() => {
  221. if (this.$refs.managerTable) {
  222. const tables = Array.isArray(this.$refs.managerTable) ? this.$refs.managerTable : [this.$refs.managerTable];
  223. tables.forEach(table => table.listData());
  224. }
  225. });
  226. },
  227. searchManager() {
  228. this.managerParam.content.pageNumber = 1;
  229. if (this.$refs.managerTable) {
  230. const tables = Array.isArray(this.$refs.managerTable) ? this.$refs.managerTable : [this.$refs.managerTable];
  231. tables.forEach(table => table.listData());
  232. }
  233. },
  234. handleManagerSelect(val, row) {
  235. this.$set(row, 'name', val.name);
  236. this.$set(row, 'hrid', val.hrid);
  237. this.$set(row, 'userid', val.userid);
  238. if (!row.timestamp) {
  239. this.$set(row, 'timestamp', new Date().getTime());
  240. }
  241. row.popoverVisible = false;
  242. },
  243. resetModelParam(row, index) {
  244. this.modelParam.content.where.condition = '';
  245. this.modelParam.content.pageNumber = 1;
  246. this.$nextTick(() => {
  247. const table = this.getTableRef(this.$refs.modelTable, index);
  248. if (table) {
  249. table.listData();
  250. }
  251. });
  252. },
  253. searchModel(row, index) {
  254. this.modelParam.content.pageNumber = 1;
  255. const table = this.getTableRef(this.$refs.modelTable, index);
  256. if (table) {
  257. table.listData();
  258. }
  259. },
  260. handleModelSelectionChange(val, row) {
  261. const models = (val || []).map(item => this.getModelValue(item)).filter(Boolean);
  262. const uniqueModels = models.filter((item, idx) => models.indexOf(item) === idx);
  263. this.$set(row, 'productModel', uniqueModels);
  264. },
  265. syncModelSelection(list, row, index) {
  266. const table = this.getTableRef(this.$refs.modelTable, index);
  267. if (!table || !table.$refs || !table.$refs.table) return;
  268. const selectedKeys = Array.isArray(row.productModel) ? row.productModel : (row.productModel ? [row.productModel] : []);
  269. (list || []).forEach(item => {
  270. const key = this.getModelValue(item);
  271. const shouldBeSelected = selectedKeys.includes(key);
  272. table.$refs.table.toggleRowSelection(item, shouldBeSelected);
  273. });
  274. },
  275. getTableRef(refs, index) {
  276. if (Array.isArray(refs)) {
  277. return refs[index];
  278. }
  279. return refs;
  280. },
  281. getModelValue(item) {
  282. return item.model || item.value || item.label || item.itemno;
  283. },
  284. formatModelLabel(models) {
  285. const list = Array.isArray(models) ? models : (models ? [models] : []);
  286. return list.join('、');
  287. },
  288. async queryData() {
  289. // TODO: 调用接口获取当前配置
  290. const res = await this.$api.requested({
  291. "content": {
  292. "where": {
  293. "condition": ""
  294. },
  295. "pageNumber": 1,
  296. "pageSize": 20
  297. },
  298. "id": 2026011709524202,
  299. });
  300. if (res.code === 1) {
  301. this.tableData = (res.data || []).map(item => ({
  302. ...item,
  303. name: item.name, // 确保有 name 字段
  304. productModel: Array.isArray(item.models) ? item.models : (item.models ? item.models.split(',') : []), // 确保 productModel 是数组
  305. popoverVisible: false,
  306. modelPopoverVisible: false
  307. }));
  308. }
  309. },
  310. async saveRow(row) {
  311. if (!row.hrid) {
  312. this.$message.warning(this.$t('请选择产品经理'));
  313. return;
  314. }
  315. const res = await this.$api.requested({
  316. "content": {
  317. "hrid": row.hrid,
  318. "userid": row.userid,
  319. "models": row.productModel,
  320. },
  321. "id": 2026011709520902,
  322. });
  323. if (res.code === 1) {
  324. this.$message.success(this.$t('保存成功'));
  325. this.queryData();
  326. this.$emit('queryRule');
  327. } else {
  328. this.$message.warning(res.msg || this.$t('保存失败'));
  329. }
  330. },
  331. setSuccess(){
  332. this.queryData()
  333. }
  334. }
  335. }
  336. </script>
  337. <style scoped>
  338. .dialog-footer {
  339. text-align: right;
  340. }
  341. /* 强制所有单元格换行 */
  342. /deep/ .el-table .cell {
  343. white-space: normal !important;
  344. }
  345. </style>