@netang/quasar 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/utils/uploader.js CHANGED
@@ -10,6 +10,7 @@ import $n_uniq from 'lodash/uniq'
10
10
  import $n_find from 'lodash/find'
11
11
  import $n_isFunction from 'lodash/isFunction'
12
12
 
13
+ import $n_storage from '@netang/utils/storage'
13
14
  import $n_isValidArray from '@netang/utils/isValidArray'
14
15
  import $n_isValidObject from '@netang/utils/isValidObject'
15
16
  import $n_isValidString from '@netang/utils/isValidString'
@@ -24,6 +25,7 @@ import $n_isValidValue from '@netang/utils/isValidValue'
24
25
  import $n_http from '@netang/utils/http'
25
26
  import $n_getThrowMessage from '@netang/utils/getThrowMessage'
26
27
  import $n_runAsync from '@netang/utils/runAsync'
28
+ import $n_numberDeep from '@netang/utils/numberDeep'
27
29
 
28
30
  import $n_$ruleValid from './$ruleValid'
29
31
  import $n_toast from './toast'
@@ -34,25 +36,76 @@ import $n_getImage from './getImage'
34
36
  import $n_getFile from './getFile'
35
37
  import $n_config from './config'
36
38
  import $n_timestamp from './timestamp'
39
+ import $n_play from './play'
37
40
 
41
+ import copy from './copy'
38
42
  import { configs } from './config'
39
43
 
40
- import copy from './copy'
44
+ /**
45
+ * 文件类型映射
46
+ */
47
+ export const FilE_TYPE = {
48
+ file: 1,
49
+ image: 2,
50
+ video: 3,
51
+ audio: 4,
52
+ }
41
53
 
42
- import {
43
- // 文件类型映射
44
- FilE_TYPE,
45
- // 文件名称映射
46
- FilE_NAME,
47
- // 上传状态
48
- UPLOAD_STATUS,
49
- // 上传至服务器
50
- uploadServer,
51
- } from './useUploader'
54
+ /**
55
+ * 文件名称映射
56
+ */
57
+ export const FilE_NAME = {
58
+ 1: '文件',
59
+ 2: '图片',
60
+ 3: '视频',
61
+ 4: '音频',
62
+ }
63
+
64
+ /**
65
+ * 上传状态
66
+ */
67
+ export const UPLOAD_STATUS = {
68
+ // 等待上传中
69
+ waiting: 1,
70
+ // 检查 hash 中
71
+ hashChecking: 2,
72
+ // 检查 hash 完成
73
+ hashChecked: 3,
74
+ // 检查是否存在服务器中
75
+ existChecking: 4,
76
+ // 检查是否存在服务器完成
77
+ existChecked: 5,
78
+ // 上传中
79
+ uploading: 6,
80
+ // 上传完成
81
+ success: 7,
82
+ // 上传失败
83
+ fail: 8,
84
+ }
52
85
 
53
86
  // 文件数量
54
87
  let _fileNum = 0
55
88
 
89
+ /**
90
+ * 将 base64 转换为 File
91
+ */
92
+ function dataUrlToFile(dataUrl) {
93
+ const arr = dataUrl.split(',')
94
+ const mime = arr[0].match(/:(.*?);/)[1]
95
+ const bstr = window.atob(arr[1])
96
+ let n = bstr.length
97
+ const arrayBuffer = new Uint8Array(n)
98
+ while (n--) {
99
+ arrayBuffer[n] = bstr.charCodeAt(n)
100
+ }
101
+ const blob = new Blob([arrayBuffer], { type: mime })
102
+
103
+ return {
104
+ arrayBuffer,
105
+ file: new File([blob], String($n_timestamp()), { type: blob.type }),
106
+ }
107
+ }
108
+
56
109
  /**
57
110
  * 创建上传器
58
111
  */
@@ -64,6 +117,8 @@ function create(options) {
64
117
  const $q = useQuasar()
65
118
 
66
119
  const {
120
+ // 上传器类型
121
+ uploaderType,
67
122
  // 上传文件输入框节点
68
123
  fileRef,
69
124
  // 更新值方法(初始化上传列表时不更新值)
@@ -72,6 +127,8 @@ function create(options) {
72
127
  onUpdate,
73
128
 
74
129
  } = Object.assign({
130
+ // 上传器类型
131
+ uploaderType: null,
75
132
  // 更新值方法
76
133
  onUpdateModelValue: null,
77
134
  // 更新方法
@@ -115,7 +172,7 @@ function create(options) {
115
172
  /**
116
173
  * 上传配置
117
174
  */
118
- const configUpload = getUpload()
175
+ const configUpload = getUpload(null, uploaderType)
119
176
 
120
177
  const configLimit = Object.assign(
121
178
  {
@@ -567,13 +624,16 @@ function create(options) {
567
624
  const hash = spark.end(false)
568
625
  if (hash) {
569
626
  // 设置文件 hash
570
- fileItem.hash = hash
627
+ fileItem.hash = await formatFileItemHash(hash)
571
628
  // 标题
572
629
  fileItem.title = title
573
630
  // 设置文件状态
574
631
  fileItem.status = UPLOAD_STATUS.hashChecked
575
632
  // 设置文件检查进度
576
633
  fileItem.progress = 100
634
+
635
+ // 设置单个文件信息
636
+ await setFileItemInfo(fileItem, setFileFail)
577
637
  return
578
638
  }
579
639
  }
@@ -645,7 +705,7 @@ function create(options) {
645
705
  // --------------------------------------------------
646
706
  const {
647
707
  formatUploadNet,
648
- } = configs
708
+ } = configs.uploader
649
709
  if ($n_isFunction(formatUploadNet)) {
650
710
  files = formatUploadNet(files, props.type === 'image')
651
711
  }
@@ -743,7 +803,7 @@ function create(options) {
743
803
  /**
744
804
  * 文件输入框更新
745
805
  */
746
- function fileChange(e) {
806
+ async function fileChange(e) {
747
807
 
748
808
  try {
749
809
  // 获取上传文件
@@ -762,7 +822,7 @@ function create(options) {
762
822
  for (const file of files) {
763
823
 
764
824
  // 创建单个文件
765
- const fileItem = createFileItem(file)
825
+ const fileItem = await createFileItem(file)
766
826
  if (fileItem !== false) {
767
827
 
768
828
  // 如果只能上传一个
@@ -889,10 +949,7 @@ function create(options) {
889
949
  // 上传至服务器
890
950
  await uploadServer({
891
951
  fileType: FilE_TYPE[props.type],
892
- configUpload,
893
952
  waitUploadFileLists,
894
- // uploadFileLists,
895
- // checkFileError,
896
953
  setFileSuccess,
897
954
  setFileFail,
898
955
  })
@@ -979,7 +1036,7 @@ function create(options) {
979
1036
  const fileReader = new FileReader()
980
1037
 
981
1038
  // 文件加载
982
- fileReader.onload = function(e) {
1039
+ fileReader.onload = async function(e) {
983
1040
 
984
1041
  // 追加 array buffer
985
1042
  spark.append(e.target.result)
@@ -1008,55 +1065,36 @@ function create(options) {
1008
1065
  return
1009
1066
  }
1010
1067
 
1011
- // 下一步
1012
- function next(hash) {
1013
- if (
1014
- // 如果开启去重
1015
- props.unique
1016
- // 如果该文件 hash 在上传文件列表中
1017
- && $n_findIndex(uploadFileLists.value, { hash }) > -1
1018
- ) {
1019
- // 轻提示
1020
- $n_toast({
1021
- message: '该文件已存在,不可重复上传',
1022
- })
1023
-
1024
- // 设置文件上传失败
1025
- setFileFail(fileItem, '已存在')
1026
-
1027
- // 删除单个文件
1028
- deleteFileItem(fileItem)
1029
-
1030
- } else {
1031
- // 设置文件 hash
1032
- fileItem.hash = hash
1033
- // 设置文件状态
1034
- fileItem.status = UPLOAD_STATUS.hashChecked
1035
- // 设置文件检查进度
1036
- fileItem.progress = 100
1037
- }
1068
+ if (
1069
+ // 如果开启去重
1070
+ props.unique
1071
+ // 如果该文件 hash 在上传文件列表中
1072
+ && $n_findIndex(uploadFileLists.value, { hash }) > -1
1073
+ ) {
1074
+ // 轻提示
1075
+ $n_toast({
1076
+ message: '该文件已存在,不可重复上传',
1077
+ })
1038
1078
 
1039
- // 完成回调
1040
- resolve()
1041
- }
1079
+ // 设置文件上传失败
1080
+ setFileFail(fileItem, '已存在')
1042
1081
 
1043
- // 格式化上传文件 hash
1044
- // --------------------------------------------------
1045
- const {
1046
- formatUploadFileHash,
1047
- } = configs
1048
- if ($n_isFunction(formatUploadFileHash)) {
1049
- $n_runAsync(formatUploadFileHash)(hash, fileItem)
1050
- .then(function (newHash) {
1051
- // 下一步
1052
- next($n_isValidString(newHash) ? newHash : hash)
1053
- })
1054
- return
1082
+ // 删除单个文件
1083
+ deleteFileItem(fileItem)
1084
+
1085
+ } else {
1086
+ // 设置文件 hash
1087
+ fileItem.hash = await formatFileItemHash(hash)
1088
+ // 设置文件状态
1089
+ fileItem.status = UPLOAD_STATUS.hashChecked
1090
+ // 设置文件检查进度
1091
+ fileItem.progress = 100
1055
1092
  }
1056
- // --------------------------------------------------
1057
1093
 
1058
- // 下一步
1059
- next(hash)
1094
+ // 设置单个文件信息
1095
+ await setFileItemInfo(fileItem, setFileFail)
1096
+
1097
+ resolve()
1060
1098
  }
1061
1099
  }
1062
1100
 
@@ -1279,7 +1317,7 @@ function create(options) {
1279
1317
  /**
1280
1318
  * 创建单个文件
1281
1319
  */
1282
- function createFileItem(file) {
1320
+ async function createFileItem(file) {
1283
1321
 
1284
1322
  // 单个文件示例
1285
1323
  // name: "123.jpg"
@@ -1344,10 +1382,11 @@ function create(options) {
1344
1382
  props.type === 'image'
1345
1383
  && file.type.toLowerCase().startsWith('image')
1346
1384
  ) {
1347
- // 获取图片预览地址
1348
- const img = new Image()
1349
- img.src = window.URL.createObjectURL(file)
1350
- fileItem.__img = img.src
1385
+ // 设置单个文件信息
1386
+ const res = await setFileItemInfo(fileItem, setFileFail)
1387
+ if (! res) {
1388
+ return false
1389
+ }
1351
1390
  }
1352
1391
 
1353
1392
  return fileItem
@@ -1510,43 +1549,6 @@ function create(options) {
1510
1549
  }
1511
1550
  }
1512
1551
 
1513
- /**
1514
- * 播放
1515
- */
1516
- function play({ hash, __img }) {
1517
-
1518
- const src = __img ? __img : $n_getFile(hash)
1519
-
1520
- let width
1521
- let height
1522
- let fullWidth = false
1523
- let fullHeight = false
1524
- let ok = true
1525
- let style = ''
1526
-
1527
- if ($q.platform.is.mobile) {
1528
- width = $q.screen.width - 48 - 32
1529
- height = $q.screen.height - 48 - 32 - 6 - 52
1530
- fullWidth = true
1531
- fullHeight = true
1532
- } else {
1533
- width = 800 - 32
1534
- height = 400 - 32 - 6
1535
- ok = false
1536
- style = 'width:800px;max-width:800px;height:400px;max-height:400px;'
1537
- }
1538
-
1539
- $q.dialog({
1540
- message: `<video style="width:${width}px;height:${height}px;" playsinline autoplay controls src="${src}" type="video/mp4" muted="muted"></video>`,
1541
- style,
1542
- html: true,
1543
- dark: true,
1544
- ok,
1545
- fullWidth,
1546
- fullHeight,
1547
- })
1548
- }
1549
-
1550
1552
  /**
1551
1553
  * 复制地址
1552
1554
  */
@@ -1563,6 +1565,472 @@ function create(options) {
1563
1565
  }
1564
1566
  }
1565
1567
 
1568
+ /**
1569
+ * ==============================【私有函数】==============================
1570
+ */
1571
+
1572
+ /**
1573
+ * 格式化单个文件 hash
1574
+ */
1575
+ async function formatFileItemHash(hash) {
1576
+
1577
+ const {
1578
+ hasMinioSuffix,
1579
+ formatUploadFileHash,
1580
+ } = configs.uploader
1581
+
1582
+ if (hasMinioSuffix && configUpload.type === 'minio') {
1583
+ hash += '_'
1584
+ }
1585
+
1586
+ // 如果有格式化上传文件 hash
1587
+ if ($n_isFunction(formatUploadFileHash)) {
1588
+ hash = await $n_runAsync(formatUploadFileHash)(hash)
1589
+ }
1590
+
1591
+ return hash
1592
+ }
1593
+
1594
+ /**
1595
+ * 获取图片信息
1596
+ */
1597
+ function getImageInfo(fileItem) {
1598
+ return new Promise(function (resolve) {
1599
+ const img = new Image()
1600
+ img.src = window.URL.createObjectURL(fileItem.file)
1601
+ fileItem.__img = img.src
1602
+ img.onload = function() {
1603
+ fileItem.json = {
1604
+ w: this.naturalWidth,
1605
+ h: this.naturalHeight,
1606
+ }
1607
+ resolve(true)
1608
+ }
1609
+ img.onerror = function() {
1610
+ resolve(false)
1611
+ }
1612
+ })
1613
+ }
1614
+
1615
+ /**
1616
+ * 获取媒体信息
1617
+ */
1618
+ function getMediaInfo(fileItem, type) {
1619
+ return new Promise(function (resolve) {
1620
+ const dom = document.createElement(type)
1621
+ dom.src = URL.createObjectURL(fileItem.file)
1622
+ dom.onerror = function() {
1623
+ resolve(false)
1624
+ }
1625
+
1626
+ // 如果为视频
1627
+ if (type === 'video') {
1628
+ dom.currentTime = 3
1629
+ dom.oncanplay = async function() {
1630
+
1631
+ fileItem.json = {
1632
+ w: this.videoWidth ? this.videoWidth : this.width,
1633
+ h: this.videoHeight ? this.videoHeight : this.height,
1634
+ d: this.duration,
1635
+ }
1636
+
1637
+ // 获取视频缩略图 hash
1638
+ const p = await getVideoThumbHash(dom, fileItem.json)
1639
+ if (p) {
1640
+ fileItem.json.p = p
1641
+ }
1642
+
1643
+ resolve(true)
1644
+ }
1645
+
1646
+ // 否则为音频
1647
+ } else {
1648
+ dom.onloadedmetadata = function() {
1649
+ fileItem.json = {
1650
+ d: this.duration,
1651
+ }
1652
+ resolve(true)
1653
+ }
1654
+ }
1655
+ })
1656
+ }
1657
+
1658
+ /**
1659
+ * 获取视频缩略图 hash
1660
+ */
1661
+ function getVideoThumbHash(dom, { w, h }) {
1662
+ return new Promise(async function (resolve) {
1663
+
1664
+ const canvas = document.createElement('canvas')
1665
+ const ctx = canvas.getContext('2d')
1666
+ canvas.setAttribute('width', w)
1667
+ canvas.setAttribute('height', h)
1668
+ ctx.drawImage(dom, 0, 0, w, h)
1669
+
1670
+ const {
1671
+ arrayBuffer,
1672
+ file,
1673
+ } = dataUrlToFile(canvas.toDataURL('image/jpeg', 0.8))
1674
+
1675
+ // 获取缩略图 hash
1676
+ const spark = new SparkMD5.ArrayBuffer()
1677
+ spark.append(arrayBuffer)
1678
+ const hash = spark.end(false)
1679
+ if (! hash) {
1680
+ resolve(false)
1681
+ return
1682
+ }
1683
+
1684
+ // 创建原始单个文件
1685
+ const fileItem = Object.assign(createRawFileItem(), {
1686
+ // 设置文件 file
1687
+ file,
1688
+ // 设置文件类型
1689
+ type: FilE_TYPE.image,
1690
+ // 设置文件后缀
1691
+ ext: 'jpg',
1692
+ // 设置文件大小
1693
+ size: file.size,
1694
+ // 设置文件 hash
1695
+ hash: await formatFileItemHash(hash),
1696
+ // 设置文件 json
1697
+ json: {
1698
+ w,
1699
+ h,
1700
+ },
1701
+ })
1702
+
1703
+ // 上传至服务器
1704
+ await uploadServer({
1705
+ fileType: FilE_TYPE.image,
1706
+ waitUploadFileLists: [ fileItem ],
1707
+ setFileSuccess(fileItem) {
1708
+ resolve(fileItem.hash)
1709
+ },
1710
+ setFileFail() {
1711
+ resolve(false)
1712
+ },
1713
+ })
1714
+ })
1715
+ }
1716
+
1717
+ /**
1718
+ * 设置单个文件信息
1719
+ */
1720
+ function setFileItemInfo(fileItem, setFileFail) {
1721
+ return new Promise(async function (resolve) {
1722
+
1723
+ // 如果已经设置过了
1724
+ if (
1725
+ $n_has(fileItem.json, 'w')
1726
+ || $n_has(fileItem.json, 'd')
1727
+ ) {
1728
+ resolve(true)
1729
+ return
1730
+ }
1731
+
1732
+ let res
1733
+
1734
+ // 如果为图片
1735
+ // --------------------------------------------------
1736
+ if (fileItem.type === FilE_TYPE.image) {
1737
+ res = await getImageInfo(fileItem)
1738
+
1739
+ // 如果为视频
1740
+ // --------------------------------------------------
1741
+ } else if (fileItem.type === FilE_TYPE.video) {
1742
+ res = await getMediaInfo(fileItem, 'video')
1743
+
1744
+ // 如果为音频
1745
+ // --------------------------------------------------
1746
+ } else if (fileItem.type === FilE_TYPE.audio) {
1747
+ res = await getMediaInfo(fileItem, 'audio')
1748
+
1749
+ // 否则为文件
1750
+ } else {
1751
+ // 先判断是否为图片
1752
+ res = await getImageInfo(fileItem)
1753
+ if (! res) {
1754
+ // 再判断是否为视频
1755
+ res = await getMediaInfo(fileItem, 'video')
1756
+ if (! res) {
1757
+ // 最后再判断是否为音频
1758
+ res = await getMediaInfo(fileItem, 'video')
1759
+ }
1760
+ }
1761
+ }
1762
+ // --------------------------------------------------
1763
+
1764
+ if (res) {
1765
+ resolve(true)
1766
+ } else {
1767
+ // 设置文件上传失败
1768
+ setFileFail(fileItem)
1769
+ resolve(false)
1770
+ }
1771
+ })
1772
+ }
1773
+
1774
+ /**
1775
+ * 获取上传参数
1776
+ */
1777
+ async function getUploadParams(type, bucket = 'public') {
1778
+
1779
+ // 缓存名
1780
+ const cacheName = `upload_params_${type}_${bucket}`
1781
+
1782
+ // 获取缓存
1783
+ const cache = $n_storage.get(cacheName)
1784
+ if (cache !== null) {
1785
+ return cache
1786
+ }
1787
+
1788
+ // 请求数据
1789
+ const { status, data } = await $n_http({
1790
+ url: $n_config('apiFileUrl') + 'get_upload_params',
1791
+ data: {
1792
+ // 类型
1793
+ type,
1794
+ // 空间名称
1795
+ bucket,
1796
+ },
1797
+ })
1798
+
1799
+ // 如果成功
1800
+ if (! status || ! $n_isValidObject(data)) {
1801
+ return false
1802
+ }
1803
+
1804
+ // 【生产模式】
1805
+ // --------------------------------------------------
1806
+ // #ifdef IS_PRO
1807
+ // 保存缓存(6 小时)
1808
+ $n_storage.set(cacheName, data, 21600000)
1809
+ // #endif
1810
+ // --------------------------------------------------
1811
+
1812
+ return data
1813
+ }
1814
+
1815
+ /**
1816
+ * 删除上传参数缓存
1817
+ */
1818
+ function deleteUploadParams(type, bucket = 'public') {
1819
+ $n_storage.delete(`upload_params_${type}_${bucket}`)
1820
+ }
1821
+
1822
+ /**
1823
+ * 上传至服务器
1824
+ */
1825
+ async function uploadServer(options) {
1826
+
1827
+ const {
1828
+ fileType,
1829
+ waitUploadFileLists,
1830
+ setFileSuccess,
1831
+ setFileFail,
1832
+ warn,
1833
+ } = Object.assign({
1834
+ warn: true,
1835
+ }, options)
1836
+
1837
+ // 获取上传参数
1838
+ const uploadParams = await getUploadParams(configUpload.type)
1839
+ if (uploadParams === false) {
1840
+ for (const fileItem of waitUploadFileLists) {
1841
+ setFileFail(fileItem, '上传失败')
1842
+ }
1843
+ if (warn) {
1844
+ $n_toast({
1845
+ message: `获取[${configUpload.type}]上传参数失败`,
1846
+ })
1847
+ }
1848
+ return false
1849
+ }
1850
+
1851
+ // 是否上传 minio 备份
1852
+ // --------------------------------------------------
1853
+ let backupParams = null
1854
+ let backupConfig = null
1855
+ if (configUpload.type !== 'minio') {
1856
+ backupConfig = getUpload(null, 'minio')
1857
+ // 如果同步
1858
+ if (backupConfig.sync === true) {
1859
+ backupParams = await getUploadParams(backupConfig.type)
1860
+ if (backupParams === false) {
1861
+ for (const fileItem of waitUploadFileLists) {
1862
+ setFileFail(fileItem, '上传失败')
1863
+ }
1864
+ if (warn) {
1865
+ $n_toast({
1866
+ message: `获取[${backupConfig.type}]上传参数失败`,
1867
+ })
1868
+ }
1869
+ return false
1870
+ }
1871
+ }
1872
+ }
1873
+ // --------------------------------------------------
1874
+
1875
+ // 批量上传
1876
+ for (const fileItem of waitUploadFileLists) {
1877
+ // 上传单个文件
1878
+ await uploadFileItem(fileItem)
1879
+ }
1880
+
1881
+ /**
1882
+ * 上传单个文件
1883
+ */
1884
+ async function uploadFileItem(fileItem) {
1885
+
1886
+ // 设置文件状态
1887
+ fileItem.status = UPLOAD_STATUS.uploading
1888
+
1889
+ // 上传文件
1890
+ const upload = async function(configUpload, uploadParams, startPercent, halfPercent) {
1891
+
1892
+ const {
1893
+ // 上传地址
1894
+ url,
1895
+ // 文件名
1896
+ fileName,
1897
+ // 键值名
1898
+ keyName,
1899
+ // 上传数据
1900
+ data,
1901
+ } = uploadParams
1902
+
1903
+ // 请求数据
1904
+ const httpData = Object.assign({}, data)
1905
+ // 文件
1906
+ httpData[fileName] = fileItem.file
1907
+ // 自定义文件 key
1908
+ httpData[keyName] = fileItem.hash
1909
+
1910
+ const { status, data: res } = await $n_http({
1911
+ // 上传地址
1912
+ url,
1913
+ // 数据
1914
+ data: httpData,
1915
+ // 关闭错误提醒
1916
+ warn: false,
1917
+ // 关闭检查结果 code
1918
+ checkCode: false,
1919
+ // 不包含头部鉴权认证
1920
+ token: false,
1921
+ // 开启上传
1922
+ upload: true,
1923
+ // 取消请求
1924
+ onCancel(cancel) {
1925
+ // 设置中断上传
1926
+ fileItem.abort = function(msg) {
1927
+ cancel($n_isValidString(msg) ? msg : '已取消')
1928
+ }
1929
+ },
1930
+ // 监听上传进度
1931
+ onUploadProgress(percent) {
1932
+ // 设置上传进度
1933
+ fileItem.progress = Math.round(startPercent + (halfPercent ? percent / 2 : percent))
1934
+ },
1935
+ })
1936
+
1937
+ // 如果请求失败
1938
+ if (! status) {
1939
+ // 设置文件上传失败
1940
+ setFileFail(fileItem, res.msg)
1941
+ // 删除上传参数缓存
1942
+ deleteUploadParams(configUpload.type)
1943
+ return false
1944
+ }
1945
+
1946
+ return res
1947
+ }
1948
+
1949
+ const resUpload = await upload(configUpload, uploadParams, 0, !! backupParams)
1950
+ if (resUpload === false) {
1951
+ return false
1952
+ }
1953
+
1954
+ // 是否已备份
1955
+ let is_backup = 0
1956
+
1957
+ // 上传至备份服务器
1958
+ // --------------------------------------------------
1959
+ if (backupParams) {
1960
+ const res = await upload(backupConfig, backupParams, 50, true)
1961
+ if (res === false) {
1962
+ return false
1963
+ }
1964
+ is_backup = 1
1965
+ }
1966
+ // --------------------------------------------------
1967
+
1968
+ const {
1969
+ title,
1970
+ type,
1971
+ hash,
1972
+ ext,
1973
+ size,
1974
+ json,
1975
+ } = fileItem
1976
+
1977
+ // 请求数据
1978
+ const data = {
1979
+ // 类型
1980
+ type: configUpload.type,
1981
+ // 需上传的文件类型
1982
+ file_type: fileType,
1983
+ // 文件
1984
+ file: {
1985
+ // 标题
1986
+ title: title || '',
1987
+ // 类型
1988
+ type,
1989
+ // hash
1990
+ hash,
1991
+ // 后缀
1992
+ ext,
1993
+ // 文件大小
1994
+ size,
1995
+ // 文件 json
1996
+ json,
1997
+ // 是否已备份
1998
+ is_backup,
1999
+ },
2000
+ // 结果
2001
+ result: $n_isValidObject(resUpload) ? resUpload : {},
2002
+ }
2003
+
2004
+ // 请求 - 上传文件至 cdn
2005
+ const { status: statusCallback, data: resCallback } = await $n_http({
2006
+ url: $n_config('apiFileUrl') + 'upload_callback',
2007
+ data,
2008
+ // 关闭错误提示
2009
+ warn: false,
2010
+ })
2011
+
2012
+ // 请求失败
2013
+ if (! statusCallback || ! $n_isValidObject(resCallback)) {
2014
+ // 设置文件上传失败
2015
+ setFileFail(fileItem, resCallback.msg || '上传失败')
2016
+ return false
2017
+ }
2018
+
2019
+ // 格式化 json
2020
+ const _json = $n_json.parse(resCallback.json)
2021
+ Object.assign(fileItem, resCallback, {
2022
+ json: $n_isValidObject(_json) ? $n_numberDeep(_json) : {},
2023
+ })
2024
+
2025
+ // 设置文件上传成功
2026
+ setFileSuccess(fileItem)
2027
+
2028
+ return true
2029
+ }
2030
+
2031
+ return true
2032
+ }
2033
+
1566
2034
  return {
1567
2035
  // 上传文件列表
1568
2036
  query: uploadFileLists,
@@ -1595,7 +2063,9 @@ function create(options) {
1595
2063
  // 修改文件名
1596
2064
  editFileTitle,
1597
2065
  // 播放视频/音频
1598
- play,
2066
+ play({ hash, __img }) {
2067
+ $n_play(__img ? __img : hash)
2068
+ },
1599
2069
  // 复制地址
1600
2070
  copyUrl,
1601
2071
  }