links.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. <template>
  2. <view class="app-page flex flex-col">
  3. <view v-if="tab.list.length > 3" class="e-fixed shadow-2">
  4. <tm-tabs color="light-blue" :shadow="0" v-model="tab.activeIndex" :list="tab.list" align="center" @change="fnOnTabChange"></tm-tabs>
  5. </view>
  6. <!-- 占位区域 -->
  7. <view v-if="tab.list.length > 3" style="width: 100vw;height: 92rpx;"></view>
  8. <view v-if="loading != 'success'" class="loading-wrap">
  9. <block v-if="loading == 'loading'">
  10. <tm-skeleton model="listAvatr"></tm-skeleton>
  11. <tm-skeleton model="listAvatr"></tm-skeleton>
  12. <tm-skeleton model="listAvatr"></tm-skeleton>
  13. <tm-skeleton model="listAvatr"></tm-skeleton>
  14. <tm-skeleton model="listAvatr"></tm-skeleton>
  15. </block>
  16. <view v-else-if="loading == 'error'" class="loading-error flex flex-col flex-center">
  17. <tm-empty icon="icon-wind-cry" label="加载失败"><tm-button theme="light-blue" size="m" @click="fnGetData()">重新加载</tm-button></tm-empty>
  18. </view>
  19. </view>
  20. <view v-else class="app-page-content">
  21. <!-- 内容区域 -->
  22. <view v-if="filterList.lenght == 0" class="empty"><tm-empty icon="icon-wind-cry" label="无数据"></tm-empty></view>
  23. <block v-else>
  24. <block v-for="(item, index) in filterList" :key="index">
  25. <tm-translate animation-name="fadeUp" :wait="calcAniWait(index)">
  26. <view class="link-card round-3 bg-white flex flex-col mt-24 ml-24 mr-24 pa-24">
  27. <view class="head flex">
  28. <view class="left round-2 flex flex-col flex-center">
  29. <image class="logo round-2 " :src="item.logo" mode="aspectFill"></image>
  30. </view>
  31. <view class="right pl-24 text-size-m">
  32. <view class="title flex flex-between ">
  33. <view class="name flex text-overflow mr-6 text-weight-b">
  34. <tm-tags :dense="true" :shadow="0" color="bg-gradient-blue-accent" size="xs" model="fill">ID:{{ item.id }}</tm-tags>
  35. <text class="text-size-l ml-12">{{ item.name }}</text>
  36. </view>
  37. <view v-if="false" class="icon text-grey-darken-2">
  38. <tm-button :shadow="0" :round="2" theme="light-blue" text size="xs" @click="fnShowFormModal(item)">修改</tm-button>
  39. <tm-button :shadow="0" :round="2" theme="red" text size="xs" @click="fnDelete(item)">删除</tm-button>
  40. </view>
  41. </view>
  42. <view class="mt-6 flex text-size-m">
  43. <view class="label text-grey-darken-2">分组:</view>
  44. <view class="value">{{ item.team || '无分组' }}</view>
  45. </view>
  46. <view class="mt-6 flex text-size-m">
  47. <view class="label text-grey-darken-2">地址:</view>
  48. <view class="value">
  49. <text>{{ item.url }}</text>
  50. <text class="ml-6 text-grey-darken-1 iconfont icon-copy" @click="$utils.copyText(item.url, '网站地址已复制')"></text>
  51. </view>
  52. </view>
  53. <view class="mt-6 flex text-size-m">
  54. <view class="label text-grey-darken-2">描述:</view>
  55. <view class="value text-overflow">{{ item.description || '该博主很懒,没有提供描述' }}</view>
  56. </view>
  57. </view>
  58. </view>
  59. <view class="foot flex flex-between mt-20 pt-16 text-size-m">
  60. <view class="e-btn update-btn flex-1 round-2 pa-12 text-blue text-align-center mr-12" @click="fnShowFormModal(item)">修改</view>
  61. <view class="e-btn del-btn flex-1 round-2 pa-12 text-red text-align-center ml-12" @click="fnDelete(item)">删除</view>
  62. </view>
  63. </view>
  64. </tm-translate>
  65. </block>
  66. </block>
  67. <tm-flotbutton @click="fnToTopPage" :offset="[16, 80]" size="m" color="light-blue" icon="icon-angle-up"></tm-flotbutton>
  68. <tm-flotbutton @click="fnShowFormModal()" size="m" color="orange" icon="icon-plus"></tm-flotbutton>
  69. <!-- 编辑或新增 -->
  70. <tm-poup v-model="poupShow" position="bottom" height="85vh" @change="fnOnPoupChange">
  71. <view class="poup-content">
  72. <view class="poup-head text-align-center text-weight-b text-size-g ma-24">{{ form.id != undefined ? '编辑友链' : '新增友链' }}</view>
  73. <scroll-view class="poup-body pa-24 pt-0" :scroll-y="true" @touchmove.stop>
  74. <tm-input required :adjust-position="true" :round="3" :borderBottom="false" title="网站名称" bg-color="grey-lighten-5" v-model="form.name" placeholder="请输入网站名称"></tm-input>
  75. <tm-input required :borderBottom="false" :adjust-position="true" :round="3" title="网站地址" bg-color="grey-lighten-5" v-model="form.url" placeholder="请输入网站地址"></tm-input>
  76. <view class="pl-32 mb-24 input-tips text-grey text-size-s">填写提示:需要加上 http://</view>
  77. <tm-input :borderBottom="false" :round="3" bg-color="grey-lighten-5" title="网站分组" placeholder="请输入选择网站分组" :value="form.team">
  78. <template v-slot:rightBtn>
  79. <tm-pickers :default-value.sync="selectTeam" :list="teamList" @confirm="fnOnSelectTeam">
  80. <tm-button class="ml-12" theme="bg-gradient-blue-accent" :round="3" :font-size="24" :height="70" block :width="120">选择</tm-button>
  81. </tm-pickers>
  82. </template>
  83. </tm-input>
  84. <tm-input input-type="number" :borderBottom="false" :adjust-position="true" :round="3" title="排序编号" bg-color="grey-lighten-5" v-model.number="form.priority" placeholder="请输入排序"></tm-input>
  85. <tm-input :borderBottom="false" :vertical="true" :adjust-position="true" inputType="textarea" :round="3" title="网站描述" :height="120" bg-color="grey-lighten-5" v-model="form.description" placeholder="请输入描述"></tm-input>
  86. <tm-input :borderBottom="false" :adjust-position="true" :round="3" title="LOGO" bg-color="grey-lighten-5" v-model="form.logo" placeholder="请输入LOGO地址"></tm-input>
  87. <view class="ma-30 mt-12 pb-12 bg-grey">
  88. <image v-if="form.logo" class="thumbnail round-3" :src="form.logo" mode="aspectFill" @click="$utils.previewImage([form.logo])"></image>
  89. <view v-else class="thumbnail round-3 text-grey grey-lighten-5 flex flex-col flex-center ">
  90. <text class="iconfont icon-picture" style="font-size: 46rpx;"></text>
  91. <text class="mt-12 text-size-m">LOGO预览图</text>
  92. </view>
  93. </view>
  94. </scroll-view>
  95. <view class="btn-wrap flex flex-center">
  96. <tm-button size="m" theme="bg-gradient-blue-accent" @click="fnSubmit()">保存</tm-button>
  97. <tm-button v-if="form.id != undefined" size="m" theme="bg-gradient-red-accent" @click="fnDelete()">删除</tm-button>
  98. <tm-button size="m" theme="bg-gradient-blue-grey-accent" @click="poupShow = false">关 闭</tm-button>
  99. </view>
  100. </view>
  101. </tm-poup>
  102. </view>
  103. </view>
  104. </template>
  105. <script>
  106. import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
  107. import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
  108. import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
  109. import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
  110. import tmPoup from '@/tm-vuetify/components/tm-poup/tm-poup.vue';
  111. import tmInput from '@/tm-vuetify/components/tm-input/tm-input.vue';
  112. import tmPickers from '@/tm-vuetify/components/tm-pickers/tm-pickers.vue';
  113. import tmTags from '@/tm-vuetify/components/tm-tags/tm-tags.vue';
  114. import tmTabs from '@/tm-vuetify/components/tm-tabs/tm-tabs.vue';
  115. import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
  116. export default {
  117. components: {
  118. tmSkeleton,
  119. tmButton,
  120. tmEmpty,
  121. tmFlotbutton,
  122. tmPoup,
  123. tmInput,
  124. tmPickers,
  125. tmTags,
  126. tmTabs,
  127. tmTranslate
  128. },
  129. data() {
  130. return {
  131. loading: 'loading',
  132. queryParams: {
  133. size: 10,
  134. page: 0
  135. },
  136. dataList: [],
  137. filterList: [],
  138. poupShow: false,
  139. form: {
  140. id: undefined,
  141. description: '',
  142. logo: '',
  143. name: '',
  144. team: '',
  145. url: '',
  146. priority: 0
  147. },
  148. tab: {
  149. activeIndex: 0,
  150. list: []
  151. },
  152. selectTeam: [],
  153. teamList: []
  154. };
  155. },
  156. onLoad() {
  157. this.fnSetPageTitle('友链管理');
  158. this.fnGetTeamData();
  159. },
  160. created() {
  161. this.fnGetData();
  162. },
  163. onPullDownRefresh() {
  164. this.fnGetData();
  165. },
  166. methods: {
  167. fnOnTabChange(index) {
  168. this.fnResetSetAniWaitIndex();
  169. this.fnToTopPage();
  170. let _filterData = [];
  171. if (index == 0) {
  172. _filterData = this.dataList;
  173. } else {
  174. _filterData = this.dataList.filter(x => x.team == this.teamList[index - 1]);
  175. }
  176. this.filterList = JSON.parse(JSON.stringify(_filterData));
  177. },
  178. fnGetTeamData() {
  179. this.$httpApi.admin
  180. .getLinkTeamList()
  181. .then(res => {
  182. if (res.status == 200) {
  183. this.tab.list = ['全部', ...res.data];
  184. this.teamList = res.data;
  185. } else {
  186. uni.$tm.toast('友链分组数据加载失败!');
  187. }
  188. })
  189. .catch(err => {
  190. uni.$tm.toast('友链分组数据加载失败!');
  191. });
  192. },
  193. fnGetData() {
  194. this.loading = 'loading';
  195. uni.showLoading({
  196. mask: true,
  197. title: '加载中...'
  198. });
  199. this.tab.activeIndex = 0;
  200. this.$httpApi.admin
  201. .getLinkList(this.queryParams)
  202. .then(res => {
  203. if (res.status == 200) {
  204. const _dataList = res.data.map(item => {
  205. item.logo = this.$utils.checkUrl(item.logo);
  206. return item;
  207. });
  208. this.dataList = _dataList;
  209. this.filterList = _dataList;
  210. this.loading = 'success';
  211. } else {
  212. this.loading = 'error';
  213. uni.$tm.toast('加载失败,请重试!');
  214. }
  215. })
  216. .catch(err => {
  217. console.error(err);
  218. this.loading = 'error';
  219. uni.$tm.toast('加载失败,请重试!');
  220. })
  221. .finally(() => {
  222. uni.hideLoading();
  223. uni.stopPullDownRefresh();
  224. });
  225. },
  226. fnOnPoupChange(e) {
  227. if (!e) {
  228. this.fnResetForm();
  229. }
  230. },
  231. fnShowFormModal(link) {
  232. if (link) {
  233. this.form = Object.assign({}, {}, link);
  234. if (link.team) {
  235. this.selectTeam = [link.team];
  236. }
  237. } else {
  238. this.fnResetForm();
  239. }
  240. this.poupShow = true;
  241. },
  242. fnOnSelectTeam(e) {
  243. this.form.team = e[0].data;
  244. this.selectTeam = [e[0].data];
  245. },
  246. fnResetForm() {
  247. this.form = {
  248. id: undefined,
  249. description: '',
  250. logo: '',
  251. name: '',
  252. team: '',
  253. url: '',
  254. priority: 0
  255. };
  256. },
  257. fnSubmit() {
  258. if (this.form.name.trim() == '') {
  259. return uni.$tm.toast('友链名称未填写!');
  260. }
  261. if (this.form.id == undefined) {
  262. this.$httpApi.admin
  263. .addLink(this.form)
  264. .then(res => {
  265. if (res.status == 200) {
  266. uni.$tm.toast(`保存成功!`);
  267. setTimeout(() => {
  268. uni.startPullDownRefresh();
  269. }, 1200);
  270. } else {
  271. uni.$tm.toast('操作失败,请重试!');
  272. }
  273. })
  274. .catch(err => {
  275. uni.$tm.toast('操作失败,请重试!');
  276. });
  277. } else {
  278. this.$httpApi.admin
  279. .updateLink(this.form.id, this.form)
  280. .then(res => {
  281. if (res.status == 200) {
  282. uni.$tm.toast(`保存成功!`);
  283. let updateIndex = this.dataList.findIndex(x => x.id == this.form.id);
  284. this.$set(this.dataList, updateIndex, this.form);
  285. } else {
  286. uni.$tm.toast('操作失败,请重试!');
  287. }
  288. })
  289. .catch(err => {
  290. uni.$tm.toast('操作失败,请重试!');
  291. });
  292. }
  293. },
  294. // 删除
  295. fnDelete(link) {
  296. const _link = link || this.form;
  297. uni.$eShowModal({
  298. title: '提示',
  299. content: `您是否要将 ${_link.name} 删除?`,
  300. showCancel: true,
  301. cancelText: '否',
  302. cancelColor: '#999999',
  303. confirmText: '是',
  304. confirmColor: '#03a9f4'
  305. })
  306. .then(res => {
  307. this.$httpApi.admin
  308. .deleteLink(_link.id)
  309. .then(res => {
  310. if (res.status == 200) {
  311. uni.$tm.toast(`${_link.name} 已删除!`);
  312. const delIndex = this.dataList.findIndex(x => x.id == _link.id);
  313. this.dataList.splice(delIndex, 1);
  314. } else {
  315. uni.$tm.toast('操作失败,请重试!');
  316. }
  317. })
  318. .catch(err => {
  319. uni.$tm.toast('操作失败,请重试!');
  320. });
  321. })
  322. .catch(err => {});
  323. }
  324. }
  325. };
  326. </script>
  327. <style lang="scss" scoped>
  328. .app-page {
  329. width: 100vw;
  330. min-height: 100vh;
  331. }
  332. .loading-wrap {
  333. padding: 24rpx;
  334. .loading-error {
  335. height: 65vh;
  336. }
  337. }
  338. .link-card {
  339. box-sizing: border-box;
  340. box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
  341. .head {
  342. .left {
  343. width: 166rpx;
  344. height: 166rpx;
  345. .logo {
  346. width: 100%;
  347. height: 100%;
  348. }
  349. }
  350. .right {
  351. width: 0;
  352. flex-grow: 1;
  353. .name {
  354. width: 0;
  355. flex-grow: 1;
  356. display: flex;
  357. align-items: center;
  358. }
  359. .label {
  360. width: 84rpx;
  361. }
  362. .value {
  363. width: 0;
  364. flex-grow: 1;
  365. }
  366. }
  367. }
  368. .foot {
  369. box-sizing: border-box;
  370. // border-top: 2rpx solid rgba(0, 0, 0, 0.03);
  371. }
  372. }
  373. .poup-content {
  374. overflow: hidden;
  375. }
  376. .poup-body {
  377. height: 71vh;
  378. box-sizing: border-box;
  379. touch-action: none;
  380. }
  381. .thumbnail {
  382. width: 100%;
  383. height: 260rpx;
  384. }
  385. .update-btn {
  386. background-color: rgba(240, 250, 255, 1);
  387. }
  388. .del-btn {
  389. background-color: rgba(254, 241, 240, 1) !important;
  390. }
  391. </style>