index.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. <template>
  2. <wrapper
  3. v-loading="loading"
  4. class="c-step"
  5. fill
  6. margin
  7. padding
  8. background
  9. >
  10. <div class="l-flex__none l-flex--row c-sibling-item--v u-color--black">
  11. <i
  12. class="l-flex__none c-sibling-item o-icon o-icon--hover el-icon-arrow-left u-pointer u-bold"
  13. @click="onBack"
  14. />
  15. <span
  16. v-if="title"
  17. class="c-sibling-item near u-font-size--md u-bold"
  18. >
  19. {{ title }}
  20. </span>
  21. </div>
  22. <status-wrapper
  23. v-if="loading || error"
  24. :error="error"
  25. @click="getWorkflow"
  26. />
  27. <template v-else>
  28. <template v-if="audit === 0">
  29. <div class="l-flex__none c-sibling-item--v">
  30. <schema-table :schema="schema" />
  31. </div>
  32. <div class="l-flex__fill l-flex c-sibling-item--v far">
  33. <div class="l-flex__none l-flex--col c-sibling-item u-width--md">
  34. <schema-table :schema="deviceSchema" />
  35. </div>
  36. <div class="l-flex__none l-flex--col c-sibling-item u-width--lg">
  37. <schema-table :schema="timeSchema" />
  38. </div>
  39. <template v-if="hasAssets">
  40. <div class="l-flex__fill l-flex--col c-sibling-item">
  41. <schema-table :schema="assetSchema" />
  42. </div>
  43. <preview-dialog ref="previewDialog" />
  44. </template>
  45. </div>
  46. </template>
  47. <div
  48. v-if="audit === 1"
  49. class="has-padding"
  50. >
  51. <el-result
  52. icon="success"
  53. title="审核成功"
  54. >
  55. <template slot="extra">
  56. <el-link
  57. type="primary"
  58. @click="onBack"
  59. >
  60. 返回列表
  61. </el-link>
  62. </template>
  63. </el-result>
  64. </div>
  65. <material-dialog ref="materialDialog" />
  66. <confirm-dialog
  67. ref="rejectDialog"
  68. title="驳回"
  69. @confirm="onConfirmReject"
  70. >
  71. <div class="c-grid-form u-align-self--center">
  72. <span class="c-grid-form__label">审核意见</span>
  73. <el-select
  74. v-model="review.type"
  75. placeholder="请选择"
  76. >
  77. <el-option
  78. v-for="option in reviewOptions"
  79. :key="option.label"
  80. :label="option.label"
  81. :value="option.value"
  82. />
  83. </el-select>
  84. <template v-if="review.type === 'reject'">
  85. <span class="c-grid-form__label u-required">原因</span>
  86. <el-input
  87. v-model.trim="review.reason"
  88. type="textarea"
  89. placeholder="请填写驳回原因"
  90. maxlength="50"
  91. :rows="3"
  92. resize="none"
  93. show-word-limit
  94. />
  95. </template>
  96. </div>
  97. </confirm-dialog>
  98. </template>
  99. </wrapper>
  100. </template>
  101. <script>
  102. import { mapGetters } from 'vuex'
  103. import {
  104. State,
  105. Access,
  106. WorkflowState,
  107. WorkflowStateInfo,
  108. PublishTargetType,
  109. EventTarget,
  110. AssetTagInfo,
  111. AssetTypeInfo
  112. } from '@/constant'
  113. import { parseDuration } from '@/utils'
  114. import { parseDeploy } from '../utils'
  115. import {
  116. getWorkflow,
  117. resolveWorkflow,
  118. rejectWorkflow,
  119. getAssetsByProgramSnap
  120. } from '../api'
  121. export default {
  122. name: 'WorkflowAudit',
  123. data () {
  124. return {
  125. loading: true,
  126. error: false,
  127. workflow: null,
  128. audit: -1,
  129. deviceSchema: {
  130. nonPagination: true,
  131. props: {
  132. size: 'small'
  133. },
  134. list: this.getDevices,
  135. cols: [
  136. { prop: 'deviceName', label: '上播设备', 'show-overflow-tooltip': false }
  137. ]
  138. },
  139. timeSchema: {
  140. nonPagination: true,
  141. props: {
  142. size: 'small'
  143. },
  144. list: this.getTimes,
  145. cols: [
  146. { prop: 'time', label: '上播时间', 'show-overflow-tooltip': false }
  147. ]
  148. },
  149. assetSchema: {
  150. nonPagination: true,
  151. props: {
  152. size: 'small'
  153. },
  154. list: this.getAssets,
  155. cols: [
  156. { prop: 'tagInfo', label: '类型', width: 72, align: 'center' },
  157. { prop: 'typeInfo', label: '资源', width: 72, align: 'center' },
  158. { prop: 'file', label: '', type: 'asset', on: this.onViewAsset },
  159. { prop: 'name', label: '' },
  160. { prop: 'adDuration', label: '上播时长', width: 80, align: 'center' },
  161. { type: 'invoke', render: [
  162. { label: '查看', allow: ({ file }) => !!file, on: this.onViewAsset }
  163. ] }
  164. ]
  165. },
  166. reviewOptions: [
  167. { value: 'reject', label: '驳回' },
  168. { value: '图文不符' },
  169. { value: '内容不合规' }
  170. ],
  171. review: {
  172. type: '',
  173. reason: ''
  174. }
  175. }
  176. },
  177. computed: {
  178. ...mapGetters(['access']),
  179. id () {
  180. return this.$route.params.id
  181. },
  182. status () {
  183. const isFinal = this.access.has(Access.REVIEW_RELEASE_FINAL)
  184. if (__JUMP_REVIEW__ && isFinal) {
  185. return [WorkflowState.FIRST_LEVEL, WorkflowState.SECOND_LEVEL, WorkflowState.FINAL_LEVEL]
  186. }
  187. const arr = []
  188. if (isFinal) {
  189. arr.push(WorkflowState.FIRST_LEVEL)
  190. }
  191. if (this.access.has(Access.REVIEW_RELEASE_SECOND)) {
  192. arr.push(WorkflowState.SECOND_LEVEL)
  193. }
  194. if (this.access.has(Access.REVIEW_RELEASE_FIRST)) {
  195. arr.push(WorkflowState.FINAL_LEVEL)
  196. }
  197. return arr
  198. },
  199. title () {
  200. if (__JUMP_REVIEW__ && this.access.has(Access.REVIEW_RELEASE_FINAL)) {
  201. return '审核'
  202. }
  203. if (this.workflow) {
  204. return WorkflowStateInfo[this.workflow.currentSeveralReviewed]
  205. }
  206. return ''
  207. },
  208. isProgram () {
  209. if (this.workflow) {
  210. return this.workflow.options.publish === PublishTargetType.EVENT && this.workflow.options.event === EventTarget.PROGRAM
  211. }
  212. return false
  213. },
  214. isAssets () {
  215. if (this.workflow) {
  216. return this.workflow.options.publish === PublishTargetType.EVENT && this.workflow.options.event === EventTarget.ASSETS
  217. }
  218. return false
  219. },
  220. needView () {
  221. if (this.workflow) {
  222. return this.workflow.options.publish === PublishTargetType.CALENDAR || this.isProgram
  223. }
  224. return false
  225. },
  226. hasAssets () {
  227. return this.isProgram || this.isAssets
  228. },
  229. schema () {
  230. return {
  231. nonPagination: true,
  232. list: this.getList,
  233. cols: [
  234. { prop: 'priority', label: '优先级', width: 100, align: 'center' },
  235. { prop: 'type', label: '上播内容', width: 80, align: 'center' },
  236. { prop: 'name', label: '' },
  237. { prop: 'ratio', label: '适配', width: 120 },
  238. { prop: 'user', label: '申请人', width: 160, align: 'center' },
  239. { type: 'invoke', render: [
  240. this.needView && { label: '查看', on: this.onView },
  241. { label: '通过', on: this.onResolve },
  242. { label: '驳回', on: this.onReject }
  243. ].filter(Boolean), width: 160 }
  244. ]
  245. }
  246. },
  247. workflowNodeId () {
  248. // 待审核节点
  249. return this.workflow?.nodes.find(({ status }) => status === State.SUBMITTED)?.id
  250. }
  251. },
  252. created () {
  253. this.getWorkflow()
  254. },
  255. methods: {
  256. getWorkflow () {
  257. this.loading = true
  258. this.error = false
  259. getWorkflow(this.id).then(
  260. ({ data }) => {
  261. if (data.status === State.SUBMITTED && this.status.includes(data.currentSeveralReviewed)) {
  262. this.workflow = {
  263. id: data.id,
  264. user: data.createUser,
  265. currentSeveralReviewed: data.currentSeveralReviewed,
  266. nodes: data.workflowNodeDtoList,
  267. ...parseDeploy(data.businessData)
  268. }
  269. this.audit = 0
  270. this.loading = false
  271. } else {
  272. this.$message({
  273. type: 'warning',
  274. message: '流程已结束或无审核权限'
  275. })
  276. this.onBack()
  277. }
  278. },
  279. () => {
  280. this.error = true
  281. this.loading = false
  282. }
  283. )
  284. },
  285. getList () {
  286. return Promise.resolve({ data: [this.workflow] })
  287. },
  288. getDevices () {
  289. return Promise.resolve({ data: this.workflow.deviceList })
  290. },
  291. getTimes () {
  292. return Promise.resolve({ data: this.workflow.targetList })
  293. },
  294. onView ({ detail }) {
  295. this.$refs.materialDialog.showPublishTarget(detail)
  296. },
  297. getAssets () {
  298. if (this.isAssets) {
  299. const assets = this.workflow.detail.detail.target.sources.map(this.transformAsset)
  300. if (assets.length === 1) {
  301. assets[0].adDuration = '独占'
  302. }
  303. return Promise.resolve({ data: Object.freeze(assets) })
  304. }
  305. if (this.isProgram) {
  306. return getAssetsByProgramSnap(this.workflow.detail.detail.target.id).then(({ data }) => {
  307. return {
  308. data: data.map(this.transformAsset)
  309. }
  310. })
  311. }
  312. return Promise.resolve({ data: [] })
  313. },
  314. transformAsset ({ tag, type, keyName, name, originalName, duration }) {
  315. if (!type) {
  316. return {
  317. name: '资源已删除'
  318. }
  319. }
  320. return {
  321. tagInfo: AssetTagInfo[tag],
  322. typeInfo: AssetTypeInfo[type],
  323. file: {
  324. type,
  325. url: keyName
  326. },
  327. name: name || originalName,
  328. adDuration: parseDuration(duration)
  329. }
  330. },
  331. onViewAsset ({ file }) {
  332. this.$refs.previewDialog.show(file)
  333. },
  334. onBack () {
  335. this.$router.replace({ name: 'workflow-list' })
  336. },
  337. resolveWorkflow () {
  338. return resolveWorkflow(this.workflowNodeId)
  339. },
  340. onResolve () {
  341. this.$confirm(
  342. '审核通过?',
  343. '操作确认',
  344. { type: 'warning' }
  345. ).then(() => {
  346. this.resolveWorkflow().then(() => {
  347. this.audit = 1
  348. })
  349. })
  350. },
  351. onReject () {
  352. this.review = {
  353. type: 'reject',
  354. reason: ''
  355. }
  356. this.$refs.rejectDialog.show()
  357. },
  358. onConfirmReject (done) {
  359. const reason = this.review.type === 'reject' ? this.review.reason : this.review.type
  360. if (!reason) {
  361. this.$message({
  362. type: 'warning',
  363. message: '请选择或填写驳回原因'
  364. })
  365. return
  366. }
  367. rejectWorkflow(this.workflowNodeId, reason).then(() => {
  368. done()
  369. this.audit = 1
  370. })
  371. }
  372. }
  373. }
  374. </script>