add.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. <template>
  2. <view class="uni-container">
  3. <uni-notice-bar style="margin: 0;" showIcon text="本页面信息,在应用发布、app升级模块中,都会关联使用,请认真填写" />
  4. <uni-forms ref="form" v-model="formData" validateTrigger="bind" style="max-width: 792px;"
  5. :labelWidth="labelWidth" :rules="rules">
  6. <uni-card title="基础信息">
  7. <uni-forms-item class="forn-item__flex" name="appid" label="AppID" required>
  8. <uni-easyinput :disabled="isEdit" placeholder="应用的AppID" v-model="formData.appid" trim="both">
  9. </uni-easyinput>
  10. </uni-forms-item>
  11. <uni-forms-item name="name" label="应用名称" required>
  12. <uni-easyinput :disabled="isEdit" placeholder="应用名称" v-model="formData.name" trim="both">
  13. </uni-easyinput>
  14. </uni-forms-item>
  15. <uni-forms-item name="introduction" label="应用简介">
  16. <uni-easyinput placeholder="应用简介" v-model="formData.introduction" trim="both"></uni-easyinput>
  17. </uni-forms-item>
  18. <uni-forms-item name="description" label="应用描述">
  19. <textarea :maxlength="-1" auto-height placeholder="应用描述"
  20. @input="binddata('description', $event.detail.value)" class="uni-textarea-border"
  21. v-model="formData.description"></textarea>
  22. </uni-forms-item>
  23. </uni-card>
  24. <uni-card title="图标素材">
  25. <uni-forms-item label="应用图标">
  26. <uni-file-picker v-model="middleware_img.icon_url" :image-styles="{'width' : '200rpx'}"
  27. return-type="object" file-mediatype="image" limit="1" mode="grid"
  28. @success="(res) => iconUrlSuccess(res,'icon_url')"
  29. @delete="(res) => iconUrlDelete(res,'icon_url')">
  30. </uni-file-picker>
  31. </uni-forms-item>
  32. <uni-forms-item label="应用截图">
  33. <uni-file-picker v-model="screenshotList" file-mediatype="image" mode="grid"
  34. :image-styles="{'height': '500rpx','width' : '300rpx'}" @delete="iconUrlDelete">
  35. </uni-file-picker>
  36. </uni-forms-item>
  37. </uni-card>
  38. <uni-card class="app_platform" title="App 信息">
  39. <view v-if="isEdit" class="extra-button">
  40. <button type="primary" plain size="mini" @click="autoFillApp">自动填充</button>
  41. <show-info :left="-10" :top="-35" width="230" content="从App升级中心同步应用安装包信息" />
  42. </view>
  43. <view v-for="item in appPlatformKeys" :key="item">
  44. <checkbox-group @change="({detail:{value}}) => {setPlatformChcekbox(item,!!value.length)}">
  45. <label class="title_padding" :class="{'font_bold':getPlatformChcekbox(item)}">
  46. <checkbox :value="item" :checked="middleware_checkbox[item]" />
  47. <text>{{appPlatformValues[item]}}</text>
  48. </label>
  49. </checkbox-group>
  50. <template v-if="getPlatformChcekbox(item)">
  51. <uni-forms-item label="名称">
  52. <uni-easyinput v-model="formData[item].name" trim="both"></uni-easyinput>
  53. </uni-forms-item>
  54. <uni-forms-item class="forn-item__flex" v-if="item === 'app_android'" label="上传apk包">
  55. <uni-file-picker v-model="appPackageInfo" file-extname="apk" :disabled="hasPackage" :provider="uniFilePickerProvider"
  56. returnType="object" file-mediatype="all" limit="1"
  57. @success="(res) => iconUrlSuccess(res, `${item}.url`)"
  58. @delete="(res) => iconUrlDelete(res,`${item}.url`)" style="flex:1;">
  59. <view class="flex">
  60. <radio-group @change="e => this.uniFilePickerProvider = e.detail.value">
  61. <view class="flex" style="flex-wrap: nowrap;">
  62. 上传至:
  63. <label>
  64. <radio value="unicloud" checked/><text>内置存储</text>
  65. </label>
  66. <label style="margin-left: 20rpx;">
  67. <radio value="extStorage" /><text>扩展存储</text>
  68. </label>
  69. </view>
  70. </radio-group>
  71. <button type="primary" size="mini" @click="selectFile" style="margin: 0 0 0 20rpx;">选择文件</button>
  72. <text style="padding: 10px;font-size: 12px;color: #666;">
  73. 上传apk到当前服务空间的云存储中,上传成功后,会自动使用云存储地址填充下载链接
  74. </text>
  75. </view>
  76. </uni-file-picker>
  77. <text v-if="hasPackage" style="padding-left: 20px;color: #a8a8a8;">
  78. {{appPackageInfo.size && Number(appPackageInfo.size / 1024 / 1024).toFixed(2) + 'M'}}
  79. </text>
  80. </uni-forms-item>
  81. <uni-forms-item :label="item === 'app_ios' ? 'AppStore' : '下载链接'">
  82. <uni-easyinput :maxlength="-1" v-model="formData[item].url" trim="both"></uni-easyinput>
  83. </uni-forms-item>
  84. <uni-forms-item v-if="item === 'app_ios'" label="获取 ABM 应用登录链接">
  85. <uni-easyinput :maxlength="-1" v-model="formData[item].abm_url" trim="both"></uni-easyinput>
  86. </uni-forms-item>
  87. </template>
  88. </view>
  89. <uni-popup ref="scheme" background-color="#fff">
  90. <view class="popup-content">
  91. <text style="font-size: 15px;font-weight: bold;">
  92. 常见的应用商店 scheme 地址
  93. </text>
  94. <view></view>
  95. <text>
  96. 应用宝:tmast://appdetails?r=XXX&pname=xxx;
  97. 小米:mimarket://details?id=com.xx.xx;
  98. 三星:samsungapps://ProductDetail/com.xx.xx;
  99. 华为:appmarket://details?id=com.xx.xx;
  100. oppo:oppomarket://details?packagename=com.xx.xx;
  101. vivo:vivomarket://details?id=com.xx.xx;
  102. </text>
  103. </view>
  104. </uni-popup>
  105. <uni-forms-item name="store_schemes" label="Android应用市场" labelWidth="120">
  106. <view style="height: 100%;">
  107. <view class="flex" style="justify-content: end;">
  108. <text class="pointer"
  109. style="text-decoration: underline;color: #666;font-size: 12px;padding-left: 10rpx;"
  110. @click="schemeDemo">常见应用商店schema汇总</text>
  111. <button type="primary" size="mini" @click="addStoreScheme"
  112. style="margin: 0 0 0 10px;">新增</button>
  113. </view>
  114. <view v-for="(item,index) in formData.store_list" :key="item.id">
  115. <uni-card title="" style="margin: 20px 0px 0px 0px;">
  116. <view style="display: flex;">
  117. <view style="padding-left: 10px;">
  118. <button type="warn" size="mini" @click="deleteStore(index, item)">删除</button>
  119. </view>
  120. </view>
  121. <uni-forms-item label="商店名称">
  122. <uni-easyinput v-model="item.name" trim="both"></uni-easyinput>
  123. </uni-forms-item>
  124. <uni-forms-item label="Scheme">
  125. <uni-easyinput :maxlength="-1" v-model="item.scheme" trim="both"></uni-easyinput>
  126. </uni-forms-item>
  127. </uni-card>
  128. </view>
  129. </view>
  130. </uni-forms-item>
  131. </uni-card>
  132. <uni-card class="mp_platform" title="小程序/快应用信息">
  133. <view v-for="item in mpPlatformKeys" :key="item">
  134. <checkbox-group @change="({detail:{value}}) => {setPlatformChcekbox(item,!!value.length)}">
  135. <label class="title_padding" :class="{'font_bold':getPlatformChcekbox(item)}">
  136. <checkbox :value="item" :checked="middleware_checkbox[item]" />
  137. <text>{{mpPlatform[item]}}</text>
  138. </label>
  139. </checkbox-group>
  140. <template v-if="mpAccordionStatus && getPlatformChcekbox(item)">
  141. <uni-forms-item label="名称">
  142. <uni-easyinput v-model="formData[item].name" trim="both"></uni-easyinput>
  143. </uni-forms-item>
  144. <uni-forms-item :label="mpPlatform[item].slice(-3) + '码'">
  145. <uni-file-picker v-model="middleware_img[item]" :image-styles="{'width' : '200rpx'}"
  146. return-type="object" file-mediatype="image" limit="1" mode="grid"
  147. @success="(res) => iconUrlSuccess(res, `${item}.qrcode_url`)"
  148. @delete="(res) => iconUrlDelete(res, `${item}.qrcode_url`)">
  149. </uni-file-picker>
  150. </uni-forms-item>
  151. </template>
  152. </view>
  153. </uni-card>
  154. <uni-card title="web信息">
  155. <uni-forms-item label="链接地址">
  156. <uni-easyinput :maxlength="-1" v-model="formData.h5.url" trim="both"></uni-easyinput>
  157. <span style="font-size: 13px; color: #999;">如需免费的前端网页托管,请开通 <a style="color: inherit;"
  158. href="https://unicloud.dcloud.net.cn">uniCloud</a> ,创建服务空间,并在 “前端网页托管”
  159. 里上传你的网页</span>
  160. </uni-forms-item>
  161. </uni-card>
  162. <uni-card :isShadow="false" v-if="isEdit">
  163. <text><text style="font-weight: bold;">提示:</text>保存后需重新生成发布页</text>
  164. </uni-card>
  165. <view class="uni-button-group">
  166. <button type="primary" class="uni-button" style="width: 100px;" @click="submit">保存</button>
  167. <navigator open-type="navigateBack" style="margin-left: 15px;">
  168. <button class="uni-button" style="width: 100px;">返回</button>
  169. </navigator>
  170. </view>
  171. </uni-forms>
  172. </view>
  173. </template>
  174. <script>
  175. // 导入 mixin 文件
  176. import mixin from './mixin/publish_add_detail_mixin.js';
  177. // 获取数据库实例
  178. const db = uniCloud.database();
  179. // 获取数据库命令实例
  180. const dbCmd = db.command;
  181. // 定义数据库集合名称
  182. const dbCollectionName = 'opendb-app-list';
  183. /**
  184. * 生成指定长度的随机字符串
  185. * @param {number} len - 随机字符串的长度
  186. * @returns {string} - 生成的随机字符串
  187. */
  188. function randomString(len) {
  189. // 设定要生成的字符串包含的字符
  190. let array = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
  191. 'U', 'V', 'W', 'X', 'Y', 'Z'
  192. ];
  193. let result = '';
  194. for (let i = 0; i < len; i++) {
  195. result += array[Math.floor(Math.random() * 26)];
  196. }
  197. return result;
  198. }
  199. export default {
  200. // mixins 导入 mixin
  201. mixins: [mixin],
  202. // 组件的数据对象
  203. data() {
  204. return {
  205. // 额外数据,初始化为空字符串
  206. mpExtra: ' ',
  207. // 手风琴状态,默认为1
  208. mpAccordionStatus: 1,
  209. // 标签宽度,默认为'80px'
  210. labelWidth: '80px',
  211. uniFilePickerProvider: 'unicloud'
  212. }
  213. },
  214. /**
  215. * 页面加载时的处理函数
  216. * @param {object} e - 传入的参数对象
  217. */
  218. onLoad(e) {
  219. if (e.id) {
  220. // 标记为编辑状态
  221. this.isEdit = true;
  222. // 设置导航栏标题为'修改应用'
  223. uni.setNavigationBarTitle({
  224. title: '修改应用'
  225. });
  226. this.setFormData('appid', e.id);
  227. this.getDetail(e.id);
  228. } else {
  229. // 填写应用名称后,给各平台设置相同的名称
  230. this.$watch('formData.name', (name) => {
  231. this.platFormKeys.forEach(key => {
  232. this.setFormData(`${key}.name`, name);
  233. });
  234. });
  235. }
  236. },
  237. onReady() {
  238. this.mpExtra = '折叠'
  239. },
  240. methods: {
  241. // 更新线上版本的 store 记录
  242. resolvestableVersionStoreList() {
  243. const modifiedMap = {}
  244. const modifiedKeys = []
  245. this.formData.store_list.forEach((item, index) => {
  246. modifiedKeys.push(item.id)
  247. modifiedMap[item.id] = index
  248. })
  249. return this.fetchAppInfo(this.getFormData('appid'), 'Android')
  250. .then(res => {
  251. if (!res.success) return
  252. if (res.store_list) {
  253. const originalMap = {}
  254. const originalKeys = []
  255. res.store_list.forEach((item, index) => {
  256. originalKeys.push(item.id)
  257. originalMap[item.id] = index
  258. })
  259. modifiedKeys.forEach((key, index) => {
  260. const afterItem = this.formData.store_list[modifiedMap[key]]
  261. // 新增
  262. if (originalKeys.indexOf(key) === -1) {
  263. res.store_list.push(afterItem)
  264. } else {
  265. // 修改
  266. res.store_list[originalMap[key]].name = afterItem.name
  267. res.store_list[originalMap[key]].scheme = afterItem.scheme
  268. }
  269. })
  270. // 删除
  271. for (let i = 0; i < res.store_list.length; i++) {
  272. let id = res.store_list[i].id
  273. if (this.deletedStore.indexOf(id) !== -1 && modifiedKeys.indexOf(id) === -1) {
  274. res.store_list.splice(i, 1)
  275. i--
  276. }
  277. }
  278. } else {
  279. res.store_list = this.formData.store_list
  280. }
  281. return this.updateAppVersion(res._id, {
  282. store_list: res.store_list
  283. })
  284. })
  285. },
  286. updateAppVersion(id, value) {
  287. // 更新应用版本
  288. return db.collection('opendb-app-versions').doc(id).update(value)
  289. },
  290. /**
  291. * 验证表单并提交
  292. */
  293. submit() {
  294. // 显示遮罩
  295. uni.showLoading({
  296. mask: true
  297. })
  298. this.formatFormData()
  299. // 表单验证
  300. this.$refs.form.validate(this.keepItems).then((res) => {
  301. // 表单提交
  302. return this.submitForm(res)
  303. }).catch((err) => {
  304. console.error(err)
  305. }).finally(() => {
  306. // 关闭遮罩
  307. uni.hideLoading()
  308. })
  309. },
  310. /**
  311. * 提交表单
  312. */
  313. submitForm(value) {
  314. (
  315. this.isEdit ?
  316. this.requestCloudFunction('setNewAppData', {
  317. id: this.formDataId,
  318. value
  319. }) :
  320. db.collection(dbCollectionName).add(value)
  321. )
  322. .then((res) => {
  323. if (this.isEdit) return this.resolvestableVersionStoreList()
  324. })
  325. .then(() => {
  326. uni.showToast({
  327. title: `${this.isEdit ? '更新' : '新增'}成功`
  328. })
  329. this.getOpenerEventChannel().emit('refreshData')
  330. setTimeout(() => uni.navigateBack(), 500)
  331. })
  332. .catch((err) => {
  333. uni.showModal({
  334. content: err.message || '请求服务失败',
  335. showCancel: false
  336. })
  337. })
  338. },
  339. /**
  340. * 获取表单数据
  341. * @param {Object} id
  342. */
  343. getDetail(id) {
  344. uni.showLoading({
  345. mask: true
  346. })
  347. db.collection(dbCollectionName).where({
  348. appid: id
  349. }).get().then((res) => {
  350. const data = res.result.data[0]
  351. if (data) {
  352. this.formDataId = data._id
  353. this.initFormData(data)
  354. } else {
  355. this.autoFill()
  356. this.autoFillApp()
  357. }
  358. }).catch((err) => {
  359. uni.showModal({
  360. content: err.message || '请求服务失败',
  361. showCancel: false
  362. })
  363. }).finally(() => {
  364. uni.hideLoading()
  365. })
  366. },
  367. // 切换手风琴状态
  368. mpAccordion() {
  369. if (this.mpAccordionStatus) {
  370. this.mpExtra = '展开'
  371. this.mpAccordionStatus = 0
  372. } else {
  373. this.mpExtra = '折叠'
  374. this.mpAccordionStatus = 1
  375. }
  376. },
  377. addStoreScheme() {
  378. this.formData.store_list.push({
  379. enable: false,
  380. priority: 0,
  381. id: randomString(5) + '_' + Date.now()
  382. })
  383. },
  384. deleteStore(index, item) {
  385. if (item.scheme && item.scheme.trim().length && this.isEdit) {
  386. uni.showModal({
  387. content: '是否同步删除线上版本此条商店记录?',
  388. success: (res) => {
  389. const storeItem = this.formData.store_list.splice(index, 1)[0]
  390. if (storeItem && res.confirm) {
  391. this.deletedStore.push(storeItem.id)
  392. }
  393. }
  394. })
  395. } else {
  396. this.formData.store_list.splice(index, 1)[0]
  397. }
  398. },
  399. schemeDemo() {
  400. // #ifndef H5
  401. $refs.scheme.open('center')
  402. // #endif
  403. // #ifdef H5
  404. window.open("https://ask.dcloud.net.cn/article/39960", '_blank')
  405. // #endif
  406. }
  407. }
  408. }
  409. </script>
  410. <style lang="scss">
  411. .title_padding {
  412. padding-bottom: 15px;
  413. display: block;
  414. }
  415. .font_bold {
  416. font-weight: bold;
  417. }
  418. .uni-button-group {
  419. & button {
  420. margin-left: 15px;
  421. }
  422. & button:first-child {
  423. margin-left: 0px;
  424. }
  425. }
  426. ::v-deep {
  427. .forn-item__flex {
  428. .uni-forms-item__content {
  429. display: flex;
  430. align-items: center;
  431. .custom-button {
  432. height: 100%;
  433. margin-left: 10rpx;
  434. line-height: 36px;
  435. }
  436. }
  437. }
  438. .uni-card {
  439. padding: 0 !important;
  440. cursor: auto;
  441. }
  442. .uni-card__header {
  443. background-color: #eee;
  444. }
  445. .uni-card__header-title-text {
  446. font-weight: bold;
  447. }
  448. }
  449. .extra-button {
  450. display: flex;
  451. align-items: center;
  452. margin-bottom: 15px;
  453. button {
  454. margin: 0;
  455. }
  456. }
  457. .flex-center-r {
  458. display: flex;
  459. align-items: center;
  460. justify-content: center;
  461. }
  462. .tip {
  463. display: flex;
  464. flex-direction: column;
  465. align-items: flex-start;
  466. background-color: #f3f5f7;
  467. color: #2c3e50;
  468. padding: 10px;
  469. font-size: 32rpx;
  470. border: {
  471. color: #e96900;
  472. left-width: 8px;
  473. left-style: solid;
  474. }
  475. text {
  476. margin-right: 15px;
  477. }
  478. .custom-button {
  479. margin-left: 0px;
  480. }
  481. }
  482. .popup-content {
  483. padding: 30rpx;
  484. }
  485. ::v-deep .uni-file-picker__files {
  486. max-width: 100%;
  487. }
  488. </style>