index.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <template>
  2. <wrapper
  3. margin
  4. padding
  5. background
  6. >
  7. <grid-table
  8. ref="table"
  9. :schema="schema"
  10. >
  11. <grid-table-item v-slot="item">
  12. <div class="l-flex--col c-program">
  13. <div
  14. class="c-program__body u-pointer"
  15. :style="item.style"
  16. @click="onClickProgram(item)"
  17. >
  18. <i
  19. v-if="item.status === 1"
  20. class="c-program__status el-icon-lock u-color--warning"
  21. />
  22. <i
  23. v-if="item.status === 2"
  24. class="c-program__status el-icon-circle-check u-color--success"
  25. />
  26. <span class="c-program__rate">
  27. {{ item.resolutionRatio }}
  28. </span>
  29. <div class="c-program__footer">
  30. <span class="c-program__time u-ellipsis">{{ item.createTime }}</span>
  31. <el-tooltip
  32. v-if="item.status === 0"
  33. content="提交"
  34. :hide-after="2000"
  35. >
  36. <i
  37. class="o-icon--active el-icon-upload2 u-pointer"
  38. @click.stop="onSubmit(item)"
  39. />
  40. </el-tooltip>
  41. <el-tooltip
  42. v-if="item.status === 2"
  43. content="复制"
  44. :hide-after="2000"
  45. >
  46. <svg-icon
  47. class="o-icon--active u-pointer"
  48. icon-class="copy"
  49. @click.stop="onCopy(item)"
  50. />
  51. </el-tooltip>
  52. <permission
  53. :skip="item.status === 0"
  54. :access="Access.DELETE_FORCE"
  55. >
  56. <el-tooltip
  57. content="删除"
  58. :hide-after="2000"
  59. >
  60. <i
  61. class="o-icon--active el-icon-delete u-pointer"
  62. @click.stop="onDel(item)"
  63. />
  64. </el-tooltip>
  65. </permission>
  66. </div>
  67. </div>
  68. <edit-input
  69. v-model.trim="item.name"
  70. class="c-program__name"
  71. :disabled="item.status !== 0"
  72. align="left"
  73. @edit="onEdit(item)"
  74. />
  75. </div>
  76. </grid-table-item>
  77. </grid-table>
  78. <confirm-dialog
  79. ref="addDialog"
  80. title="新增节目"
  81. @confirm="onConfirmAdd"
  82. >
  83. <div class="c-grid-form u-align-self--center">
  84. <span class="c-grid-form__label required">名称:</span>
  85. <el-input
  86. v-model.trim="program.name"
  87. placeholder="请填写节目名称"
  88. maxlength="50"
  89. show-word-limit
  90. />
  91. <span class="c-grid-form__label required">分辨率:</span>
  92. <schema-select
  93. v-model="program.resolutionRatio"
  94. placeholder="请选择分辨率"
  95. :schema="resolutionRatioSelectSchema"
  96. />
  97. </div>
  98. </confirm-dialog>
  99. <confirm-dialog
  100. ref="copyDialog"
  101. title="复制节目"
  102. @confirm="onConfirmCopy"
  103. >
  104. <div class="c-grid-form u-align-self--center">
  105. <span class="c-grid-form__label required">名称:</span>
  106. <el-input
  107. v-model.trim="copyProgram.name"
  108. placeholder="请填写节目名称"
  109. maxlength="50"
  110. show-word-limit
  111. />
  112. </div>
  113. </confirm-dialog>
  114. </wrapper>
  115. </template>
  116. <script>
  117. import { getAssetUrl } from '@/api/asset'
  118. import { getRatios } from '@/api/device'
  119. import {
  120. getPrograms,
  121. addProgram,
  122. updateProgramName,
  123. deleteProgram,
  124. getProgram,
  125. submitProgram,
  126. copyProgram
  127. } from '@/api/program'
  128. import { State } from '@/constant'
  129. import { validate } from './core/utils'
  130. export default {
  131. name: 'BigScreen',
  132. data () {
  133. return {
  134. program: {},
  135. copyProgram: {
  136. id: null,
  137. name: ''
  138. },
  139. resolutionRatioSelectSchema: { remote: this.getRatios },
  140. schema: {
  141. condition: {
  142. status: void 0,
  143. resolutionRatio: void 0,
  144. name: ''
  145. },
  146. list: getPrograms,
  147. buttons: [
  148. { type: 'add', on: this.onAdd }
  149. ],
  150. filters: [
  151. { key: 'resolutionRatio', type: 'select', placeholder: '全部分辨率', simple: true, remote: getRatios },
  152. { key: 'status', type: 'select', placeholder: '全部状态', options: [
  153. { value: State.READY, label: '待提交' },
  154. { value: State.SUBMITTED, label: '未审核' },
  155. { value: State.RESOLVED, label: '已审核' }
  156. ] },
  157. { key: 'name', type: 'search', placeholder: '节目名称' }
  158. ],
  159. transform: this.transform
  160. }
  161. }
  162. },
  163. created () {
  164. window.addEventListener('message', this.onMessage)
  165. },
  166. beforeDestroy () {
  167. window.removeEventListener('message', this.onMessage)
  168. },
  169. methods: {
  170. onMessage (event) {
  171. if (!this.loading) {
  172. const id = event.data?.id
  173. if (id) {
  174. const program = this.$refs.table.getData().find(program => program.id === id)
  175. if (program) {
  176. program.style = this.getStyle(event.data.base64, true)
  177. }
  178. }
  179. }
  180. },
  181. getRatios (params) {
  182. return getRatios(params).then(({ data }) => {
  183. return {
  184. data: data.map(({ value, label, devices }) => {
  185. return {
  186. value,
  187. label: `${label} ${devices}`
  188. }
  189. })
  190. }
  191. })
  192. },
  193. transform ({ id, name, status, resolutionRatio, createTime, img }) {
  194. return {
  195. id, name, status, resolutionRatio,
  196. style: this.getStyle(img),
  197. createTime: createTime?.split(' ')[0],
  198. originalName: name
  199. }
  200. },
  201. getStyle (img, base64) {
  202. return img ? {
  203. backgroundSize: 'contain',
  204. backgroundImage: `url("${base64 || /^data/.test(img) ? img : getAssetUrl(img)}")`
  205. } : {}
  206. },
  207. onEdit (item) {
  208. if (!item.name) {
  209. item.name = item.originalName
  210. return
  211. }
  212. if (item.name === item.originalName) {
  213. return
  214. }
  215. updateProgramName({
  216. id: item.id,
  217. name: item.name
  218. }).then(() => {
  219. item.originalName = item.name
  220. }, () => {
  221. item.name = item.originalName
  222. })
  223. },
  224. onAdd () {
  225. this.program = {
  226. name: '',
  227. resolutionRatio: ''
  228. }
  229. this.$refs.addDialog.show()
  230. },
  231. onConfirmAdd (done) {
  232. if (!this.program.name) {
  233. this.$message({
  234. type: 'warning',
  235. message: '名称不能为空'
  236. })
  237. return
  238. }
  239. if (!this.program.resolutionRatio) {
  240. this.$message({
  241. type: 'warning',
  242. message: '请选择分辨率'
  243. })
  244. return
  245. }
  246. addProgram(this.program).then(({ data: id }) => {
  247. done()
  248. this.$refs.table.resetCondition({ status: State.READY, name: this.program.name })
  249. this.onDesign(id)
  250. })
  251. },
  252. onDesign (id) {
  253. window.open(this.$router.resolve({
  254. name: 'program',
  255. params: { id }
  256. }).href, '_blank')
  257. },
  258. onView (id) {
  259. window.open(this.$router.resolve({
  260. name: 'program',
  261. params: { id }
  262. }).href, '_blank')
  263. },
  264. onClickProgram (item) {
  265. const { id, status } = item
  266. if (status === State.READY) {
  267. this.onDesign(id)
  268. } else {
  269. this.onView(id)
  270. }
  271. },
  272. onSubmit (item) {
  273. const loading = this.$showLoading()
  274. getProgram(item.id).then(({ data }) => {
  275. const { itemJsonStr } = data
  276. if (!itemJsonStr) {
  277. this.$message({
  278. type: 'warning',
  279. message: `请先编辑节目`
  280. })
  281. return
  282. }
  283. try {
  284. const error = validate(JSON.parse(itemJsonStr))
  285. if (error) {
  286. this.$message({
  287. type: 'warning',
  288. message: error
  289. })
  290. return
  291. }
  292. } catch (e) {
  293. this.$message({
  294. type: 'warning',
  295. message: `节目数据异常`
  296. })
  297. return
  298. }
  299. submitProgram(item).then(() => {
  300. this.$refs.table.resetCondition({ status: State.SUBMITTED })
  301. })
  302. }).finally(() => {
  303. this.$closeLoading(loading)
  304. })
  305. },
  306. onDel (item) {
  307. deleteProgram(item).then(() => {
  308. this.$refs.table.decrease(1)
  309. })
  310. },
  311. onCopy ({ id, name }) {
  312. this.copyProgram = { id, name }
  313. this.$refs.copyDialog.show()
  314. },
  315. onConfirmCopy (done) {
  316. const { id, name } = this.copyProgram
  317. copyProgram({ id }, name).then(() => {
  318. done()
  319. this.$refs.table.resetCondition({ status: State.READY, name })
  320. })
  321. }
  322. }
  323. }
  324. </script>
  325. <style lang="scss" scoped>
  326. .c-program {
  327. &__body {
  328. position: relative;
  329. padding-top: 60%;
  330. color: #fff;
  331. font-size: 16px;
  332. border-radius: $radius;
  333. background: rgba(0, 0, 0, 0.8) url("~@/assets/program_bg.png") center center /
  334. 100% 100% no-repeat;
  335. overflow: hidden;
  336. }
  337. &__status {
  338. position: absolute;
  339. top: 6px;
  340. left: 6px;
  341. font-size: 24px;
  342. }
  343. &__rate {
  344. position: absolute;
  345. top: 0;
  346. right: 0;
  347. padding: 2px 4px;
  348. font-size: 12px;
  349. background-color: rgba(0, 0, 0, 0.5);
  350. border-radius: 0 0 0 4px;
  351. }
  352. &__name {
  353. color: $black;
  354. font-weight: bold;
  355. }
  356. &__footer {
  357. display: inline-flex;
  358. align-items: center;
  359. position: absolute;
  360. width: 100%;
  361. bottom: 0;
  362. padding: 24px 16px 16px;
  363. background-image: linear-gradient(
  364. to bottom,
  365. rgba(#000, 0) 0%,
  366. rgba(#000, 0.6) 100%
  367. );
  368. .o-icon--active {
  369. margin-left: $spacing;
  370. font-size: 18px;
  371. }
  372. }
  373. &__time {
  374. flex: 1 1 auto;
  375. min-width: 0;
  376. font-size: 14px;
  377. }
  378. }
  379. </style>