图书馆小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

387 lines
10 KiB

2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
2 months ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <view class="reader-card">
  3. <image class="card-top-bg" src="@/static/images/card-img1.png" mode="widthFix" />
  4. <view class="edit-btn" @click="toggleEditMode">
  5. {{ isEditMode ? '完成' : '解绑' }}
  6. </view>
  7. <view class="card-list">
  8. <view
  9. class="card-list-item"
  10. v-for="(item, index) in cardList"
  11. :key="item.id"
  12. @click="handleSelectItem(item.bindValue)"
  13. :class="{ active: selectedValue === item.bindValue || (!isEditMode && item.bindDefault) }"
  14. >
  15. <image class="card-left-img" src="@/static/images/card-img2.png" mode="widthFix" />
  16. <view class="card-right-info">
  17. <text class="info-title">读者证号</text>
  18. <text class="info-num">{{ formatCardNo(item.bindValue) }}</text>
  19. <text class="default-tag" v-if="item.bindDefault">默认证</text>
  20. </view>
  21. <!-- 编辑模式显示单选框 -->
  22. <radio v-if="isEditMode" :value="item.bindValue" :checked="selectedValue === item.bindValue"/>
  23. <!-- 非编辑模式才显示设置默认 + 二维码 -->
  24. <view class="card-setting" v-else>
  25. <button v-if="!item.bindDefault" type="default" size="mini" @click.stop="setDefaultCard(item.bindValue)">设置默认</button>
  26. <uni-icons class="erweima-style" custom-prefix="iconfont" type="icon-erweima" size="28" @click.stop="showQrcode(item.bindValue)"></uni-icons>
  27. </view>
  28. </view>
  29. <view class="add-card-btn" @click="toAddReaderCard" v-if="!isEditMode">
  30. <uni-icons type="plus" size="20" color="#01a4fe"></uni-icons>
  31. <text>绑定读者证</text>
  32. </view>
  33. </view>
  34. <!-- 编辑模式显示底部解绑按钮 -->
  35. <button
  36. v-if="isEditMode"
  37. class="unbind-btn"
  38. :disabled="!selectedValue"
  39. :class="{disabled: !selectedValue}"
  40. @click="handleUnbind"
  41. >
  42. 确认解绑
  43. </button>
  44. <!-- 二维码弹窗 -->
  45. <uni-popup ref="popup" type="center" @maskClick="closePopup">
  46. <view class="qrcode-popup">
  47. <view class="qrcode-title">读者证二维码</view>
  48. <w-qrcode
  49. v-if="qrcodeText"
  50. :value="qrcodeText"
  51. :size="160"
  52. foreground="#333"
  53. background="#f5f5f5"
  54. />
  55. <view class="qrcode-num">{{ qrcodeText }}</view>
  56. <!-- <view class="close-btn" @click="closePopup">关闭</view> -->
  57. </view>
  58. </uni-popup>
  59. <button v-if="!isEditMode" class="register-btn" type="primary" @click="goRegister">在线办证</button>
  60. </view>
  61. </template>
  62. <script>
  63. import {
  64. FetchFindAllReaderBindByOpenId,
  65. FetchSetDefaultReadCard,
  66. FetchUnbindReadCard
  67. } from '@/api/user';
  68. import { getReaderCardList, getOpenId, setCurrentReaderCard, clearReaderCardCache, STORAGE_KEYS } from '@/utils/storage';
  69. import config from '@/utils/config';
  70. export default {
  71. data() {
  72. return {
  73. isEditMode: false,
  74. selectedValue: '',
  75. cardList: [],
  76. qrcodeText: '',
  77. loading: false,
  78. }
  79. },
  80. onShow() {
  81. this.getBindReaderCardList();
  82. },
  83. methods: {
  84. formatCardNo(cardNo) {
  85. if (!cardNo) return '';
  86. const str = String(cardNo);
  87. const len = str.length;
  88. if (len > 8) {
  89. const front = str.substring(0, 2);
  90. const end = str.substring(len - 2);
  91. const star = '*'.repeat(len - 4);
  92. return front + star + end;
  93. } else {
  94. return '***' + str.substring(len - 2);
  95. }
  96. },
  97. toggleEditMode() {
  98. this.isEditMode = !this.isEditMode;
  99. this.selectedValue = '';
  100. },
  101. async getBindReaderCardList(forceRefresh = false) {
  102. if (this.loading) return;
  103. this.loading = true;
  104. try {
  105. const readerList = await getReaderCardList(forceRefresh);
  106. this.cardList = readerList;
  107. } catch (err) {
  108. console.error(err);
  109. const cachedList = uni.getStorageSync(STORAGE_KEYS.READER_CARD_LIST) || [];
  110. this.cardList = cachedList;
  111. } finally {
  112. this.loading = false;
  113. }
  114. },
  115. handleSelectItem(value) {
  116. if (this.isEditMode) {
  117. this.selectedValue = this.selectedValue === value ? '' : value;
  118. }
  119. },
  120. async setDefaultCard(value) {
  121. const currentDefault = this.cardList.find(item => item.bindDefault === true);
  122. if (currentDefault && currentDefault.bindValue === value) {
  123. uni.showToast({ title: '当前已是默认证', icon: 'none' });
  124. return;
  125. }
  126. const openId = await getOpenId();
  127. if (!openId) {
  128. uni.showToast({ title: '获取用户信息失败', icon: 'none' });
  129. return;
  130. }
  131. uni.showModal({
  132. title: '提示',
  133. content: `确定设置【${value}】为默认读者证吗?`,
  134. success: async (res) => {
  135. if (!res.confirm) return;
  136. try {
  137. await FetchSetDefaultReadCard({
  138. bindType: 'rdid',
  139. bindValue: value,
  140. libcode: config.LIB_CODE,
  141. openid: openId
  142. });
  143. setCurrentReaderCard(value);
  144. await this.getBindReaderCardList(true);
  145. uni.showToast({ title: '设置成功', icon: 'success' });
  146. } catch (err) {
  147. console.error(err);
  148. uni.showToast({ title: '设置失败', icon: 'none' });
  149. }
  150. }
  151. });
  152. },
  153. showQrcode(bindValue) {
  154. this.qrcodeText = bindValue;
  155. this.$refs.popup.open();
  156. },
  157. closePopup() {
  158. this.qrcodeText = '';
  159. this.$refs.popup.close();
  160. },
  161. async handleUnbind() {
  162. if (!this.selectedValue) {
  163. uni.showToast({ title: '请选择要解绑的读者证', icon: 'none' });
  164. return;
  165. }
  166. const openId = await getOpenId();
  167. if (!openId) {
  168. uni.showToast({ title: '获取用户信息失败', icon: 'none' });
  169. return;
  170. }
  171. const unbindItem = this.cardList.find(item => item.bindValue === this.selectedValue);
  172. const isUnbindDefault = unbindItem?.bindDefault === true;
  173. uni.showModal({
  174. title: '提示',
  175. content: `确定要解绑读者证【${this.selectedValue}】吗?`,
  176. success: async (res) => {
  177. if (!res.confirm) return;
  178. try {
  179. const result = await FetchUnbindReadCard({
  180. bindType: "rdid",
  181. bindValue: this.selectedValue,
  182. libcode: config.LIB_CODE,
  183. openid: openId
  184. });
  185. if (result.code === 200) {
  186. uni.showToast({ title: '解绑成功', icon: 'success' });
  187. this.selectedValue = '';
  188. await this.getBindReaderCardList(true);
  189. if (isUnbindDefault && this.cardList.length > 0) {
  190. const newValue = this.cardList[0].bindValue;
  191. await FetchSetDefaultReadCard({
  192. bindType: 'rdid',
  193. bindValue: newValue,
  194. libcode: config.LIB_CODE,
  195. openid: openId
  196. });
  197. setCurrentReaderCard(newValue);
  198. await this.getBindReaderCardList(true);
  199. }
  200. if (this.cardList.length === 0) {
  201. clearReaderCardCache();
  202. setTimeout(() => uni.navigateBack(), 1000);
  203. }
  204. }
  205. } catch (err) {
  206. console.error(err);
  207. uni.showToast({ title: '解绑失败', icon: 'none' });
  208. }
  209. }
  210. });
  211. },
  212. toAddReaderCard() {
  213. uni.navigateTo({ url: "/pages/login/login" });
  214. },
  215. // 跳转到在线办证页面
  216. goRegister() {
  217. uni.navigateTo({
  218. url: '/pages/register/register'
  219. });
  220. }
  221. }
  222. }
  223. </script>
  224. <style lang="scss" scoped>
  225. .reader-card{
  226. position: relative;
  227. min-height: 100vh;
  228. background-color: #f5f5f5;
  229. .card-top-bg{
  230. position: absolute;
  231. left: 0;
  232. top: 0;
  233. width: 100%;
  234. }
  235. .edit-btn{
  236. position: absolute;
  237. right: 20px;
  238. top: 20px;
  239. z-index: 100;
  240. font-size: 15px;
  241. }
  242. .card-list{
  243. position: absolute;
  244. left: 0;
  245. top: 60px;
  246. width: calc(100% - 24px);
  247. height: calc(100vh - 195px);
  248. overflow-y: auto;
  249. padding: 12px;
  250. z-index: 999;
  251. .card-list-item{
  252. display: flex;
  253. justify-content: space-between;
  254. align-items: center;
  255. padding: 10px;
  256. margin-bottom: 12px;
  257. border: 2px solid #C6C6E2;
  258. border-radius: 8px;
  259. background: rgba(241,241,249,0.4);
  260. cursor: pointer;
  261. transition: all 0.2s;
  262. &.active{
  263. border-color: #01a4fe;
  264. background: rgba(1, 164, 254, 0.1);
  265. }
  266. .card-left-img{
  267. width: 80px;
  268. margin-right: 12px;
  269. display: block;
  270. }
  271. .card-right-info{
  272. flex: 1;
  273. display: flex;
  274. flex-direction: column;
  275. .info-title{
  276. font-size: 16px;
  277. color: #333;
  278. font-weight: bold;
  279. padding-bottom: 4px;
  280. }
  281. .info-num{
  282. font-size: 14px;
  283. color: #999;
  284. }
  285. .default-tag{
  286. width: 46px;
  287. font-size: 12px;
  288. color: #01a4fe;
  289. border:1px solid #01a4fe;
  290. border-radius: 4px;
  291. padding: 2px 0;
  292. text-align: center;
  293. display: inline-block;
  294. margin-top: 4px;
  295. }
  296. }
  297. }
  298. }
  299. .add-card-btn {
  300. display: flex;
  301. align-items: center;
  302. justify-content: center;
  303. margin-top: 15px;
  304. height: 44px;
  305. border: 1px dashed #01a4fe;
  306. border-radius: 8px;
  307. color: #01a4fe;
  308. font-size: 15px;
  309. uni-icons {
  310. margin-right: 6px;
  311. }
  312. }
  313. .unbind-btn{
  314. position: fixed;
  315. bottom: 40px;
  316. left: 0;
  317. right: 0;
  318. width: calc(100% - 40px);
  319. margin: 0 auto;
  320. padding: 4px 0;
  321. color: #fff;
  322. background-color: #01a4fe;
  323. border-radius: 23px;
  324. font-size: 16px;
  325. &.disabled{
  326. background-color: #ccc;
  327. }
  328. }
  329. }
  330. .card-setting{
  331. display: flex;
  332. flex-direction: column;
  333. align-items: flex-end;
  334. justify-content: center;
  335. button{
  336. padding: 0 8px !important;
  337. margin-bottom: 16px;
  338. font-size: 12px;
  339. }
  340. ::v-deep .icon-erweima{
  341. color: #999 !important;
  342. }
  343. }
  344. </style>