CFlowPath.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. <template>
  2. <layout title="流程">
  3. <div class="custom-flow-path-container">
  4. <!-- 审批人 -->
  5. <div class="rows">
  6. <div class="left">
  7. <div class="left__title">
  8. 审批人
  9. </div>
  10. <div class="left__desc">
  11. {{ approveTxt }}
  12. </div>
  13. </div>
  14. <div class="right flex flex-row">
  15. <div v-if="approveSelList.length" class="procesbox flex flex-row flex-row-aic">
  16. <div class="flex flex-row" v-for="(personal, idx) in approveSelList" :key="idx">
  17. <div class="personal flex flex-col">
  18. <div class="avatar avatar--name">
  19. <template v-if="personal.avatar">
  20. <img class="avatar-heade" :src="personal.avatar" />
  21. </template>
  22. <template v-else>
  23. <span class="avatar__name">{{ personal.name | changeName }}</span>
  24. </template>
  25. </div>
  26. <div class="personal__name">{{ personal.name }}</div>
  27. </div>
  28. <van-icon class="arrow" v-if="idx !== approveSelList.length - 1" :size="14" name="arrow"
  29. color="rgb(151, 151, 151)" />
  30. </div>
  31. </div>
  32. <!-- <div v-if="approveSelList.length < 3" class="empty-box" @click="handleOpenContacts">
  33. <van-icon :size="20" color="#979797" name="plus" />
  34. </div> -->
  35. </div>
  36. <div class="rows-line"></div>
  37. </div>
  38. <!-- 抄送人 -->
  39. <div class="rows" v-if="copySelList.length || isAllowCopy == '1'">
  40. <div class="left">
  41. <div class="left__title">
  42. 抄送人
  43. </div>
  44. <div class="left__desc">
  45. {{ sendTxt }}
  46. </div>
  47. </div>
  48. <div class="right flex flex-row">
  49. <div class="procesbox flex flex-row flex-row-aic">
  50. <div class="flex flex-row" v-if="copySelList.length > 3" @click="showMoreCopy = true">
  51. <div class="personal flex flex-col">
  52. <div class="avatar avatar--name">
  53. <span class="avatar__name">
  54. <van-icon name="friends-o" :size="24" />
  55. </span>
  56. </div>
  57. <div class="personal__name">查看全部</div>
  58. </div>
  59. <van-icon class="arrow" :size="14" name="plus" color="rgb(151, 151, 151)" />
  60. </div>
  61. <div class="flex flex-row" v-for="(personal, idx) in copySelListCompu" :key="idx">
  62. <div class="personal flex flex-col">
  63. <div class="avatar avatar--name">
  64. <template v-if="personal.avatar">
  65. <img class="avatar-heade" :src="personal.avatar" />
  66. </template>
  67. <template v-else>
  68. <span class="avatar__name">{{ personal.name | changeName }}</span>
  69. </template>
  70. </div>
  71. <div class="personal__name">{{ personal.name }}</div>
  72. </div>
  73. <van-icon v-if="idx !== copySelListCompu.length - 1" class="arrow" :size="14" name="plus"
  74. color="rgb(151, 151, 151)" />
  75. <van-icon v-else-if="isAllowCopy == '1'" class="arrow" :size="14" name="plus"
  76. color="rgb(151, 151, 151)" />
  77. </div>
  78. </div>
  79. <div class="empty-box" v-if="isAllowCopy == '1'" @click="handleOpenContactsCopy">
  80. <van-icon :size="20" color="#979797" name="plus" />
  81. </div>
  82. </div>
  83. </div>
  84. </div>
  85. <!-- NOTE: 当抄送人过多时 展示优化 -->
  86. <van-popup v-model="showMoreCopy" position="bottom" :style="{ height: '60%' }" closeable
  87. close-icon-position="top-right">
  88. <div class="show-more-copy-container flex flex-col">
  89. <div class="header">抄送{{ copySelList.length }}人</div>
  90. <div class="main">
  91. <div class="procesbox flex flex-row flex-row-aic">
  92. <div class="flex flex-row" v-for="(personal, idx) in copySelList" :key="idx">
  93. <div class="personal flex flex-col">
  94. <div class="closebox" @click="handleRemoveSignal(personal, idx)"><van-icon name="cross"
  95. :size="14" color="#fff" /></div>
  96. <div class="avatar avatar--name">
  97. <template v-if="personal.avatar">
  98. <img class="avatar-heade" :src="personal.avatar" />
  99. </template>
  100. <template v-else>
  101. <span class="avatar__name">{{ personal.name | changeName }}</span>
  102. </template>
  103. </div>
  104. <div class="personal__name">{{ personal.name }}</div>
  105. </div>
  106. <van-icon class="arrow" v-if="idx !== copySelList.length - 1" :size="14" name="plus"
  107. color="rgb(151, 151, 151)" />
  108. </div>
  109. </div>
  110. </div>
  111. </div>
  112. </van-popup>
  113. </layout>
  114. </template>
  115. <style lang="less" scoped>
  116. @import url("@/styles/variables.less");
  117. .custom-flow-path {
  118. &-container {
  119. padding: 10px 12px;
  120. background-color: @white;
  121. .rows {
  122. position: relative;
  123. display: flex;
  124. flex-direction: row;
  125. // align-items: center;
  126. justify-content: space-between;
  127. margin-bottom: 16px;
  128. .rows-line {
  129. position: absolute;
  130. left: -10px;
  131. top: 17px;
  132. width: 1px;
  133. height: 100%;
  134. background-color: #D8D8D8;
  135. }
  136. .left {
  137. &__title {
  138. position: relative;
  139. font-size: @font-size-common;
  140. font-weight: 400;
  141. color: #191A1E;
  142. line-height: 18px;
  143. &::after {
  144. position: absolute;
  145. content: "";
  146. left: -12px;
  147. top: 50%;
  148. width: 8px;
  149. height: 8px;
  150. border-radius: 8px;
  151. background: #D8D8D8;
  152. transform: translateY(-50%);
  153. }
  154. }
  155. &__desc {
  156. font-size: @font-size-third;
  157. font-weight: 400;
  158. color: #9A9A9A;
  159. line-height: 18px;
  160. }
  161. }
  162. .empty-box {
  163. display: flex;
  164. flex-direction: row;
  165. align-items: center;
  166. justify-content: center;
  167. width: 31px;
  168. height: 31px;
  169. background: #FFFFFF;
  170. border-radius: 5px;
  171. border: 1px solid #EEEEEF;
  172. }
  173. // .right {}
  174. &:last-child {
  175. .rows-line {
  176. width: 0;
  177. }
  178. }
  179. }
  180. }
  181. }
  182. .procesbox {
  183. flex-wrap: wrap;
  184. .avatar {
  185. position: relative;
  186. width: 31px;
  187. height: 31px;
  188. background: #3290C4;
  189. border-radius: 4px;
  190. font-size: @font-size-third;
  191. &--name {
  192. display: flex;
  193. flex-direction: row;
  194. align-items: center;
  195. justify-content: center;
  196. }
  197. &-heade {
  198. width: 100%;
  199. height: 100%;
  200. vertical-align: middle;
  201. border-radius: 4px;
  202. }
  203. &__name {
  204. font-size: @font-size-third;
  205. font-family: PingFangSC-Regular, PingFang SC;
  206. font-weight: 400;
  207. color: #FFFFFF;
  208. line-height: 9px;
  209. white-space: nowrap;
  210. }
  211. }
  212. .personal {
  213. position: relative;
  214. align-items: center;
  215. &__name {
  216. font-size: @font-size-third;
  217. font-family: PingFangSC-Regular, PingFang SC;
  218. font-weight: 400;
  219. color: #9A9A9A;
  220. text-align: center;
  221. line-height: 20px;
  222. }
  223. .closebox {
  224. position: absolute;
  225. right: 0;
  226. top: 0;
  227. z-index: 9;
  228. font-size: 0;
  229. transform: translate(30%, -30%);
  230. background-color: #b7b5b5;
  231. border-radius: 1000px;
  232. padding: 1px;
  233. }
  234. }
  235. .arrow {
  236. height: 31px;
  237. line-height: 31px;
  238. padding: 0 5px;
  239. }
  240. }
  241. .show-more-copy-container {
  242. .header {
  243. font-size: 12px;
  244. line-height: 50px;
  245. padding: 0 20px;
  246. color: #828282;
  247. }
  248. .main {
  249. height: 0;
  250. flex: 1;
  251. padding: 6px 10px;
  252. }
  253. }
  254. </style>
  255. <script>
  256. import Layout from './Layout.vue';
  257. import { dingtalkComplexPicker } from '@/utils/dingtalk'
  258. export default {
  259. name: 'CFlowPath',
  260. components: {
  261. Layout
  262. },
  263. computed: {
  264. approveTxt() {
  265. let list = this.approveSelList
  266. return list.length ? `${list.length}人依次审批` : '请选择审批人'
  267. },
  268. sendTxt() {
  269. let list = this.copySelList
  270. return list.length ? `抄送${list.length}人` : '请选择抄送人'
  271. },
  272. copySelListCompu() {
  273. let arrs = [...this.copySelList]
  274. if (arrs.length > 2) return arrs.slice(-2)
  275. else return arrs
  276. },
  277. },
  278. props: {
  279. approve: {
  280. type: Array
  281. },
  282. copy: {
  283. type: Array
  284. },
  285. isAllowCopy: { // 是否允许抄送人存在变更 (0:否, 1:是)
  286. // validator: val => (['0', '1'].includes(val)),
  287. type: [String, Number],
  288. default: 1
  289. }
  290. },
  291. data() {
  292. return {
  293. showMoreCopy: false,
  294. approveSelList: [],
  295. copySelList: []
  296. }
  297. },
  298. methods: {
  299. // 打开钉钉联系人控件。完成选审批/抄送人操作
  300. async handleOpenContacts() {
  301. const result = await dingtalkComplexPicker({})
  302. console.log(result);
  303. },
  304. async handleOpenContactsCopy() {
  305. if (this.isAllowCopy != 1) return
  306. const result = await dingtalkComplexPicker({})
  307. console.log(result);
  308. },
  309. handleRemoveSignal(person, idx) {
  310. this.copySelList.splice(idx, 1)
  311. // TODO: Update copy data.
  312. }
  313. },
  314. watch: {
  315. approve: {
  316. handler(arrs) {
  317. if (arrs.length) this.approveSelList = [...arrs]
  318. },
  319. deep: true,
  320. },
  321. copy: {
  322. handler(arrs) {
  323. if (arrs.length) this.copySelList = [...arrs]
  324. },
  325. deep: true
  326. }
  327. }
  328. }
  329. </script>