index.vue 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452
  1. <template>
  2. <div class="login-bg">
  3. <div class="left-box">
  4. <div class="left-box-title">
  5. <div style="display: flex; align-items: center">
  6. <img src="@/assets/login/logo.png" style="width: 49px; height: 44px" />
  7. <span class="left-box-title-text">U店在这</span>
  8. </div>
  9. <div class="left-box-title-bottom-text">登录U店在这,轻松管店、快速核销</div>
  10. </div>
  11. <img src="@/assets/login/login.png" class="login-class" />
  12. <div class="qrcode-box">
  13. <img src="@/assets/login/udzzApp.png" />
  14. <div style="width: 124px; height: 124px; background: black" />
  15. <span class="qrcode-box-text">扫码下载</span>
  16. </div>
  17. </div>
  18. <div class="right-box">
  19. <div class="right-box-title">
  20. 欢迎登录
  21. <img src="@/assets/login/ud.png" style="width: 74px; height: 74px" />
  22. 在这
  23. </div>
  24. <el-tabs v-model="activeTab" class="login-tabs">
  25. <el-tab-pane label="密码登录" name="password">
  26. <el-form ref="passwordFormRef" :model="passwordForm" :rules="passwordRules" size="large" class="login-form-content">
  27. <el-form-item prop="phone" label="手机号码" label-position="top">
  28. <el-input v-model="passwordForm.phone" placeholder="请输入手机号" maxlength="11" clearable />
  29. </el-form-item>
  30. <el-form-item prop="password" label="密码" label-position="top">
  31. <el-input
  32. v-model="passwordForm.password"
  33. type="password"
  34. placeholder="请输入密码"
  35. show-password
  36. autocomplete="new-password"
  37. maxlength="16"
  38. clearable
  39. @input="handlePasswordInput($event, 'passwordForm', 'password')"
  40. />
  41. </el-form-item>
  42. <el-form-item prop="captcha" label="登录验证" label-position="top">
  43. <div class="captcha-wrapper">
  44. <el-input
  45. v-model="passwordForm.captcha"
  46. placeholder="请输入图片中的数字"
  47. maxlength="4"
  48. clearable
  49. class="captcha-input"
  50. />
  51. <div class="captcha-image" @click="refreshCaptcha">
  52. <img v-if="captchaImage" :src="captchaImage" alt="验证码" />
  53. <div v-else class="captcha-placeholder">点击获取</div>
  54. </div>
  55. </div>
  56. </el-form-item>
  57. </el-form>
  58. </el-tab-pane>
  59. <el-tab-pane label="验证码登录" name="code">
  60. <el-form ref="codeFormRef" :model="codeForm" :rules="codeRules" size="large" class="login-form-content">
  61. <el-form-item prop="phone" label="手机号码" label-position="top">
  62. <el-input v-model="codeForm.phone" placeholder="请输入手机号" maxlength="11" clearable />
  63. </el-form-item>
  64. <el-form-item prop="code" label="验证码" label-position="top">
  65. <div class="code-wrapper">
  66. <el-input v-model="codeForm.code" placeholder="请输入验证码" maxlength="6" clearable class="code-input" />
  67. <el-button :disabled="loginCodeCountdown > 0" @click="sendLoginCode" class="code-button">
  68. {{ loginCodeCountdown > 0 ? `${loginCodeCountdown}秒后重试` : "获取验证码" }}
  69. </el-button>
  70. </div>
  71. </el-form-item>
  72. </el-form>
  73. </el-tab-pane>
  74. </el-tabs>
  75. <!-- 登录按钮 -->
  76. <el-button type="primary" size="large" :loading="loading" @click="handleLogin" class="login-button"> 登录 </el-button>
  77. <!-- 底部链接 -->
  78. <div class="form-footer">
  79. <span class="link-text" @click="handleForgotPassword">忘记密码?</span>
  80. <span class="link-text" @click="openRegister">还没有账号,去注册</span>
  81. </div>
  82. <!-- 用户协议 -->
  83. <div class="agreement-wrapper">
  84. <el-checkbox v-model="agreed" />
  85. <span class="agreement-text">
  86. 我已阅读并同意
  87. <span class="link-text" @click="handleUserAgreement">《用户协议》</span>
  88. <span class="link-text" @click="handlePrivacyPolicy">《隐私政策》</span>
  89. </span>
  90. </div>
  91. </div>
  92. <el-dialog v-model="userShow">
  93. <template #header>
  94. <div>
  95. <p style="display: flex; align-items: center; justify-content: center">“U 店在这” 商家端用户协议</p>
  96. </div>
  97. </template>
  98. <div style="height: 50vh; padding: 10px; overflow-y: auto; font-size: 14px; line-height: 1.6">
  99. <p>
  100. 您在使用爱丽恩严(大连)商务科技有限公司旗下U店在这APP软件提供的服务前,应当仔细认真阅读本《服务条款》(下称"本条款")中的全部规则、《用户协议》及发布的其他服务条款、专项产品或服务规则或规范的内容,尤其是以粗体或加下划线标示的条款,包括但不限于免除或者限爱丽恩严(大连)商务科技有限公司责任的条款、对用户权利进行限制的条款以及约定争议解决方式、司法管辖的条款,上述条款请您重点阅读。您有权选择同意或者不同意本协议。
  101. </p>
  102. <p><strong>本协议所称的爱丽恩严</strong>是指爱丽恩严(大连)商务科技有限公司的简称。</p>
  103. <p>
  104. 您与爱丽恩严均应当严格履行本协议及其补充协议所约定的各项义务,如发生争议或者纠纷,双方可以友好协商解决;协商不成的,任何一方均可向本协议签订地有管辖权的人民法院提起诉讼。本协议签订地为大连市中山区。
  105. </p>
  106. <p>
  107. 您如果通过登录U店在哪APP用户注册页面或者爱丽恩严提供的其他用户注册渠道注册用户账号,完成我们的注册流程并通过点击同意的形式在线签署本协议即视为您完全同意本协议,愿意接受本协议所有及任何条款的约束。
  108. </p>
  109. <p>所有服务规则视为本条款不可分割的一部分,与本条款具有同等法律效力。</p>
  110. <p>
  111. <strong>本条款的签约双方</strong>为爱丽恩严服务的实际运营商爱丽恩严(大连)商务科技有限公司(下称"爱丽恩严")
  112. 与使用爱丽恩严相关服务的使用人(以下称"用户"或"您"),本条款是您与爱丽恩严之间关于您使用爱丽恩严提供的各项服务所订立的服务条款,具有正式书面合同的效力。
  113. </p>
  114. <p>
  115. 本条款为爱丽恩严平台《用户协议》(包括但不限于所附的《隐私协议》)
  116. 的必要组成部分。《用户协议》将同时适用于爱丽恩严的各项服务。如本条款与《用户协议》文本内容存在冲突之处,则以时间上最新发布的内容为准,发布时间相同的,以本条款为准。本条款有待明确、存在歧义或未规定之处均以《用户协议》中的规定为准。
  117. </p>
  118. <p>
  119. 您理解并同意,爱丽恩严将根据《用户协议》的约定,对本条款或各项服务规则不时地进行修改更新。修改更新内容的发布和实施均适用《用户协议》的相关约定。
  120. </p>
  121. <h3>一、名词解释</h3>
  122. <p>除您与爱丽恩严另有约定外,本协议及其补充协议当中的下列名词均采用如下解释:</p>
  123. <p>
  124. 1.
  125. 服务条款:即本协议,指您与爱丽恩严当下订立的旨在约定您登录、使用本平台,通过本平台下达订单、购买商品/服务、支付价款以及爱丽恩严运用自己的平台系统,通过互联网络等方式为用户提供商户信息、点评信息、消费信息、优惠信息、团购等整个网络服务过程中,您与爱丽恩严之间的权利、义务的书面合同。
  126. </p>
  127. <p>
  128. 2. 爱丽恩严:指是爱丽恩严(大连)商务科技有限公司运营的爱丽恩严生活服务信息平台,包括但不限于
  129. 爱丽恩严网站、爱丽恩严客户端、爱丽恩严小程序等形式的互联网平台。
  130. </p>
  131. <p>
  132. 3.
  133. 商户:是指根据本条款及其他适用的服务规则,在爱丽恩严平台上发布信息为爱丽恩严用户提供各项线上及线下的商品或服务的第三方商家。
  134. </p>
  135. <p>
  136. 4.
  137. 团购:团购是指通过爱丽恩严平台,一定数量的用户组团或者参团,以较低折扣价格购买同一种服务或商品的行为,包括消费买单、预约及预订服务项目等。
  138. </p>
  139. <p>
  140. 5.
  141. 团购信息:是指商户通过爱丽恩严发布的团购商品或服务的信息,此类信息包括但不限于团购商品或服务的名称、种类、数量、质量、价格、配送方式、支付形式、退换货方式、退款条件、售后服务等内容。
  142. </p>
  143. <p>
  144. 6.
  145. 团购券:是指用户通过爱丽恩严购买商户的服务或商品并成功支付团购价款后,相关商户通过爱丽恩严自动向用户出具的供用户向该商户要求提供商品或服务的交易凭证。
  146. </p>
  147. <p>
  148. 7.
  149. 优惠券:是指用户通过爱丽恩严购买商户的服务或商品时,购买、免费领取或使用积分兑换用于享受减免、折扣或其他优惠条件的电子交易凭证。
  150. </p>
  151. <p>
  152. 8. 交互信息服务:交互信息服务是指用户在爱丽恩严应用上交互平台(下称 "点评交互平台
  153. ")上发布文字、图片、视频及表演(直播)等信息。点评交互平台包括但不限于点评社区、点评头条、 点评直播、笔记功能等。
  154. </p>
  155. <h3>二、用户账号</h3>
  156. <p>
  157. 1.在注册、管理、使用账号时,您应遵循诚实信用、合法善意的原则。您向平台提交的相关注册资料应当遵守法律法规、社会主义制度、国家利益、公民合法权益、公序良俗、信息真实等原则,不应提交任何违法或不良信息。相关资料如有变动,您应及时更新。如果因您所提供的注册资料不合法、不真实、不准确或未及时更新,从而导致相关法律责任或不利后果的,您将承担相
  158. 应的法律责任及不利后果。同时,爱丽恩严有权拒绝为您提供注册服务。
  159. </p>
  160. <p>2.您同意并承诺以合法、合规、合理的方式使用爱丽恩严账号:</p>
  161. <ul>
  162. <li>
  163. (1)您设置的账号昵称、头像及个人介绍等个人资料不得出现违法和不良信息,包括但不限于使用政治、色情、低俗、侮辱、诽谤等违反法律、道德及公序良俗的词语。
  164. </li>
  165. <li>
  166. (2)未经他人许可,您不得使用他人名义(包括但不限于冒用他人姓名、名称、字号、头像、身份等或采取其他足以让人引起混淆的方式)开设爱丽恩严账号。
  167. </li>
  168. <li>
  169. (3)不得假冒、仿冒、捏造党政军机关、企事业单位、新闻媒体等组织机构名称、标识信息或采取其他足以让人引起混淆的方式开设爱丽恩严账号。
  170. </li>
  171. <li>
  172. (4)您不得恶意注册爱丽恩严账号(包括但不限于频繁注册、批量注册账号等行为)或将账号用于非法或不正当用途(包括但不限于流量作假等行为),不得实施任何侵害国家利益、损害其他公民合法权益,有害社会道德风尚的行为,不得采取各种技术手段恶意绕开或者对抗平台规则。
  173. </li>
  174. </ul>
  175. <p>
  176. 3.您理解并同意,您的爱丽恩严账号的所有权及有关权益均归爱丽恩严所有,您仅享有该账号的使用权且仅限于您本人使用。为保证账号安全,未经爱丽恩严的书面同意,您不应将爱丽恩严账号以赠与、转让、出售、出借或其他方式许可他人使用,否则您应当承担由此产生的全部责任,爱丽恩严保留拒绝提供相应服务、冻结或收回注册账号或终止本服务协议的权利,并可要求您对爱丽恩严所承受的损失予以赔偿。
  177. </p>
  178. <p>
  179. 4.您应妥善保管账号信息、账号密码以及其他与账号相关的信息、资料。您有责任维护个人账号、密码的安全性与保密性,并对您以注册账号名义所从事的活动承担全部法律责任,包括但不限于您在爱丽恩严平台进行的任何信息发表、浏览点击等操作行为可能引起的一切法律责任。若发现他人未经许可使用您的账号或发生其他任何安全漏洞问题时,您应当立即通知爱丽恩严。
  180. </p>
  181. <p>
  182. 5.您理解并同意,如您违反上述条款,爱丽恩严有权对您采取要求限期改正、禁止注册、删除或屏蔽违法违规信息、不同时限的禁止信息发布和账号信息修改、封号、注销账号等处置措施。
  183. </p>
  184. <p>
  185. 6.您理解并同意,您可通过注册新的账号或登录您现有的账号以使用爱丽恩严的各项服务及功能,您使用爱丽恩严账号应同时遵守《用户协议》中的各项规定。
  186. </p>
  187. <h3>三、用户管理</h3>
  188. <p>
  189. 1.您知悉、理解并同意,爱丽恩严在服务过程中,可能涉及收集、存储、使用、共享和保护用户个人信息。在您使用爱丽恩严提供的服务时,您同意爱丽恩严依据《隐私协议》的规定执行相关个人信息的收集、使用和共享。您进一步同意,就爱丽恩严平台所产生的交易或其他与个人信息使用紧密相关的交易,您授权爱丽恩严使用或允许爱丽恩严许可的第三方在必要、合理的限度内使用您的个人信息,包括但不限于身份信息、账号信息、交易信息等。
  190. </p>
  191. <p>
  192. 2.您知悉并同意,爱丽恩严将按照有权网络信息主管部门的相关规定对您的账号信息及所发布的各项信息进行必要的安全审查及评估,保留并向有关部门定期报送您的账号信息及您所发布的各项信息。
  193. </p>
  194. <p>
  195. 3.用户有权在爱丽恩严平台上发布客观、真实、亲身体验的文字点评、图片点评、视频点评或者自行添加商户、完善爱丽恩严平台中商户的商户名称、营业时间、位置等信息。用户同意并理解,为了遵守法律法规和政策,维护良好的平台秩序,爱丽恩严有权对特定用户、特定行业、特定商品或者服务的点评信息或者商户信息发布流程作出特别设定或者限制。
  196. </p>
  197. <p>
  198. 4.您可以通过正式的页面公告和/或站内信和/或电子邮件和/或客服电话和/或手机短信、常规的信件接收和查看中奖、优惠等活动或信息,并在爱丽恩严上自行浏览、下载和使用优惠券和/
  199. 或团购券。
  200. </p>
  201. <p>
  202. 5.用户知晓并同意,他人可能通过"认领"等操作获得用户在爱丽恩严平台中添加的商户条目之管理权限,并可能对用户添加、完善之信息进行修改或者使用。
  203. </p>
  204. <p>6.用户有权根据爱丽恩严相关规定,在发布点评信息等贡献后,取得爱丽恩严给予的奖励(如贡献值、积分等)。</p>
  205. <p>
  206. 7.用户在发布信息时,您应确保该等信息的真实性、客观性、合法性,爱丽恩严提倡用户贡献高质量点评。用户应确保在爱丽恩严上发表的各类点评信息、攻略或文章、图片及视频均不涉及侵犯第三方隐私、著作权或其他合法权益。用户需维护点评的客观性,不得利用爱丽恩严用户身份进行任何违反法律、法规、国家政策以及诚实信用原则的行为,包括但不限于:
  207. </p>
  208. <ul>
  209. <li>(1)违反法律法规及国家政策的行为:</li>
  210. <li>①反对宪法所确定的基本原则的;</li>
  211. <li>②危害国家安全,泄露国家秘密,颠覆国家政权,破坏国家统一的;</li>
  212. <li>③损害国家荣誉和利益的;</li>
  213. <li>④歪曲、丑化、亵渎、否定英雄烈士事迹和精神,以侮辱、诽谤或者其他方式侵害英雄烈士的姓名、肖像、名誉、荣誉的;</li>
  214. <li>⑤宣扬恐怖主义、极端主义或者煽动实施恐怖活动、极端主义活动的;</li>
  215. <li>⑥煽动民族仇恨、民族歧视,破坏民族团结的;</li>
  216. <li>⑦破坏国家宗教政策,宣扬邪教和封建迷信的;</li>
  217. <li>⑧散布谣言,扰乱经济秩序和社会秩序的;</li>
  218. <li>⑨散布淫秽、色情、赌博、暴力、凶杀、恐怖或者教唆犯罪的;</li>
  219. <li>⑩侮辱或者诽谤他人,侵害他人名誉、隐私和其他合法权益的;</li>
  220. <li>⑪法律、行政法规禁止的其他内容。</li>
  221. <li>(2)违反诚实信用原则的行为:</li>
  222. <li>①炒作并向商户收取费用或获取利益;</li>
  223. <li>②为获得利益或好处,参与或组织撰写及发布虚假点评;</li>
  224. <li>③以差评威胁、要求商户提供额外的利益或好处;</li>
  225. <li>④以虚构事实、侮辱、诽谤等方式恶意诋毁爱丽恩严网或商家的商誉;</li>
  226. <li>⑤进行其他其它影响点评客观、干扰扰乱爱丽恩严正常秩序的违规行为等。</li>
  227. </ul>
  228. <p>如您存在以上行为的,爱丽恩严有权采取如下行动:</p>
  229. <ul>
  230. <li>①屏蔽违规信息,视情节轻重情况发出警告;</li>
  231. <li>②清除其所有评价、图片,屏蔽其发布的涉及商业性炒作的帖子;</li>
  232. <li>③暂时限制/永久限制您使用账号在爱丽恩严平台上发布信息;</li>
  233. <li>④法律、行政法规、本条款及《用户协议》规定的其他方式。</li>
  234. </ul>
  235. <p>
  236. 8.禁止用户将爱丽恩严以任何形式作为从事各种非法活动的场所、平台或媒介。未经爱丽恩严的授权或许可,用户不得借用爱丽恩严的名义从事任何商业活动,也不得以任何形式将爱丽恩严作为从事商业活动的场所、平台或媒介。
  237. </p>
  238. <p>
  239. 9.您违反本条款、《用户协议》或爱丽恩严发布的其他任何服务规则,则爱丽恩严有权在法律允许的范围内采取一切必要的措施,包括但不限于删除用户发布的内容、取消用户在爱丽恩严获得的用户积分、星级、荣誉以及虚拟财富,暂停或终止您对爱丽恩严账号的使用,给爱丽恩严造成损失的,您应负全部赔偿责任,包括且不限于财产损害赔偿、名誉损害赔偿、诉讼费、律师费、公证费、交通费等因维权而产生的合理费用。爱丽恩严有权按照本条款及《用户协议》的相关规定对您的行为进行处理。
  240. </p>
  241. <h3>四、团购服务特别约定</h3>
  242. <p>1.您有权通过爱丽恩严平台浏览各项团购信息,购买团购券、优惠券并使用其参与相关商户提供的商品或服务。</p>
  243. <p>
  244. 2.您知悉并同意,您在爱丽恩严平台上所浏览及购买的团购券、优惠券或其他消费产品均系商户所提供的商品或服务,如您因购买或使用该等商品或服务产生任何问题或争议,您应自行与相应商户协商解决。
  245. </p>
  246. <h3>五、信息评价等特别约定</h3>
  247. <p>
  248. 1.您应确保具有相应的资格并符合在爱丽恩严开展信息评价的相应条件。
  249. 如果因为您不具备相应资格或者条件的情形下通过爱丽恩严平台发布信息而引发任何法律责任,您应自行承担。因此给爱丽恩严平台造成损失的,您应予以全额赔偿。
  250. </p>
  251. <p>
  252. 2.平台有可能在相关法律法规和政策的指引下,对评价、帖子等信息发布实施分级分类管理,建立发布者信用等级管理体系,建立黑名单管理制度等。
  253. </p>
  254. <p>3.用户在平台发布的文字、图片、视频、表演(直播)等信息均应遵守相关法律法规、规章、本条款及《用户协议》的相关规定。</p>
  255. <p>4.您同意:如出现以下任一情形的,爱丽恩严将视情节轻重采取删除信息、限制平台使用功能、停止您的账号使用等处理措施:</p>
  256. <ul>
  257. <li>(1)发布违反相关法律法规、规章、政策之规定或者违反本条款及相关服务规则的信息;</li>
  258. <li>(2)发布与主题无关的信息或评论,包含文字、图片、视频等;</li>
  259. <li>(3)发布可能存在交易风险的外部网站和APP信息,如发布社交、团购等外部网站或 APP的名称、超链接、二维码等信息;</li>
  260. <li>
  261. (4)发布的视频违反国家法律法规的相关要求,包括但不限于《广播电视管理条例》、《电影管理条例》、《互联网视听节目服务管理规定》、《广电总局关于加强互联网视听节目内容管理的通知》等的相关要求;
  262. </li>
  263. <li>
  264. (5)用户进行表演(直播)时违反国家法律法规的相关要求,包括但不限于《互联网文化管理暂行规定》、《互联网直播服务管理规定》等的相关要求;
  265. </li>
  266. <li>(6)其他违反及《用户协议》相关规定的情形。</li>
  267. </ul>
  268. <h3>六、关于服务终止的约定</h3>
  269. <p>
  270. 1.您同意爱丽恩严有权随时修改或中断其向您提供的任何免费服务而无需事先通知您。您与爱丽恩严进行的有偿交易,您同意爱丽恩严有权在事先通知的情况下予以修改、中断,并按照公平、诚实信用、等价有偿的原则处理后续事宜。
  271. </p>
  272. <p>
  273. 2.如您的账号同时符合以下条件,则爱丽恩严有权利终止您对爱丽恩严账号的使用。这将导致您的账号不能再登录爱丽恩严,相应服务亦同时终止:
  274. </p>
  275. <ul>
  276. <li>(1)连续六个月未登录;</li>
  277. <li>(2)不存在未到期的有效业务;</li>
  278. <li>(3)终止您的账号和服务的行为不违反相关法律法规的强制性规定。</li>
  279. </ul>
  280. <p>
  281. 3.如您对本条款及其不时修订有任何异议的,您有权停止使用爱丽恩严的各项服务,或通过客服等渠道告知爱丽恩严停止对您提供服务。停止服务后,除法律法规另有明确规定外,爱丽恩严有权(但无义务)保留您的账号访问爱丽恩严的相关信息和数据,或留存、转发任何账号内的任何站内信或短消息。在此情况下,爱丽恩严没有义务(但有权利)向您或代为向商家或其他第三方传送任何未处理的信息或未完成的服务或交易信息。您同意爱丽恩严不就终止爱丽恩严服务而对您或任何第三方承担任何责任。
  282. </p>
  283. <p>
  284. 4.您同意,您与爱丽恩严的合同关系终止后,爱丽恩严就您在使用爱丽恩严服务期间存在违法行为或违反本条款和/或其他服务规则的行为的,
  285. 仍可依据本条款向您主张权利。
  286. </p>
  287. <h3>七、知识产权及其它权利</h3>
  288. <p>1.用户确认其已经仔细阅读并同意爱丽恩严关于知识产权等相关权利的一切声明。</p>
  289. <p>
  290. 2.爱丽恩严平台及相关服务的运营系统由爱丽恩严自主开发、运营并提供技术支持,爱丽恩严对平台服务的开发和运营等过程中产生的所有数据和信息等享有全部权利。爱丽恩严提供各项服务时所依托软件的著作权、专利权,所使用的各项商标、商业形象、商业标识、技术诀窍,其著作权、商标权及其他各项相关权利均归爱丽恩严所有。
  291. </p>
  292. <p>
  293. 3,您理解并同意,您在爱丽恩严发表的各项点评信息、文章、视频、反馈意见等所有信息及其衍生品的知识产权及所有权,适用《用户协议》中的相关约定。
  294. </p>
  295. <h3>八、免责事由</h3>
  296. <p>
  297. 1.除非爱丽恩严以书面形式明确约定,爱丽恩严对于用户以任何方式(包括但不限于包含、经
  298. 由、连接或下载)从爱丽恩严所获得的任何由商户发布的内容信息,包括但不限于商户信息、点
  299. 评内容等,不保证其准确性、完整性、可靠性。用户应当自行审核判断相关信息,并对于用户因
  300. 爱丽恩严上的内容信息而购买、获取的任何产品、服务、信息或资料自行承担责任和风险。用户
  301. 因此受损的,爱丽恩严在法律允许的最大范围内予以免责。爱丽恩严内所有用户所发表的用户
  302. 点评、商户或者产品信息等,仅代表用户个人观点,并不表示爱丽恩严赞同其观点或证实其描
  303. 述,爱丽恩严在法律允许的最大范围内予以免责。
  304. </p>
  305. <p>
  306. 2.您同意并理解,针对商户向您销售/提供的商品/服务,爱丽恩严并非您所购买的具体商品或者服务的生产者和销售者。您同意,针对该等商品或者服务以及相关售后服务中所产生的任何矛盾和纠纷均不应针对爱丽恩严提出。在法律允许的范围内,爱丽恩严不对上述商品/服务承担包括解释说明、赔偿在内的任何责任。
  307. </p>
  308. <p>
  309. 3.您在爱丽恩严平台上传、发布任何信息或者内容的,应当自行保留备份。爱丽恩严不对用户所发布信息的保存、修改、删除或储存失败负责,对爱丽恩严上的非因爱丽恩严故意所导致的排字错误、疏忽等不承担责任。爱丽恩严有权但无义务,改善或更正爱丽恩严任何部分之疏漏、错误。
  310. </p>
  311. <p>
  312. 4.您知悉爱丽恩严在其平台上所公布的各项排行榜单系根据用户真实评价、访问次数、浏览时长等数据自行生成,并无人工干预。该等榜单仅为您选择商品或服务提供参考,不涉及任何商户付费宣传,您应自行判断榜单中所列商品及服务的属性,爱丽恩严将不对此榜单中所列商品和服务做出任何形式的担保或保证。
  313. </p>
  314. <p>5.任何非经爱丽恩严正规渠道获得的中奖、优惠等活动或信息,爱丽恩严不承担法律责任。</p>
  315. <p>
  316. 6.爱丽恩严因发现爱丽恩严上显现的团购、预订、消费买单等交易产品信息明显错误或缺货时,有权单方面作出修改,但该等修改不应视为爱丽恩严对您作出的任何承诺。
  317. </p>
  318. <p>
  319. 7.爱丽恩严有权在法律允许范围内,在不通知您的情况下删除任何用户发布的不符合本条款及各项服务规则、《用户协议》以及法律规定的信息。爱丽恩严将不对删除该等信息给您造成的不便或损失承担任何责任。
  320. </p>
  321. <p>
  322. 8.您同意,在法律许可范围内,爱丽恩严在任何情况下都不对任何个人或实体的直接、间接、偶然、特殊、惩罚性的损害或其他损害或损失承担责任,这些损害或损失包括但不限于:
  323. </p>
  324. <ul>
  325. <li>(1)您理解并同意,由于互联网的特殊性造成的爱丽恩严显示的信息所存在的滞后性或差错,爱丽恩严对此不承担任何责任;</li>
  326. <li>
  327. (2)您在使用爱丽恩严服务中,因第三方原因使您遭受侮辱、诽谤、不作为、淫秽、色情或亵渎事件,爱丽恩严在法律允许的范围内不承担法律责任;
  328. </li>
  329. <li>(3)《用户协议》中规定其他不可抗力及免责事由。</li>
  330. </ul>
  331. <h3>九、管辖、法律适用与争议解决</h3>
  332. <p>1.本协议的成立、生效、履行、解释与纠纷解决,适用中华人民共和国法律法规,并且排除一切冲突法规定的适用。</p>
  333. <p>
  334. 2.您同意并理解,如您因通过爱丽恩严平台购买的任何商品或者服务产生任何纠纷的,爱丽恩严可在法律法规要求的范围内协助您与争议对方进行协商调解。您同意,爱丽恩严有权向争议双方了解情况,并将所了解的情况通过必要方式通知对方。但您理解并同意,爱丽恩严无任何义务对您与商家之间的任何争议承担任何责任。
  335. </p>
  336. <p>
  337. 3.您与爱丽恩严均应当严格履行本协议及其补充协议所约定的各项义务,如发生争议或者纠纷,双方可以友好协商解决;协商不成的,任何一方均可向本协议签订地有管辖权的法院提起诉讼。
  338. </p>
  339. <p>本协议签订地:大连市中山区。</p>
  340. <h3>十、意见及反馈</h3>
  341. <p>如您对本协议有任何问题或建议,请在工作时间联系爱丽恩严客服部门。联系方式如下:</p>
  342. <ul>
  343. <li>您可以通过网站(ailien.shop)/App上提供的在线联系方式/客服系统与我们联系;</li>
  344. </ul>
  345. </div>
  346. <template #footer>
  347. <div style="display: flex; align-items: center; justify-content: center; width: 100%">
  348. <el-button
  349. @click="userShow = false"
  350. style="width: 406px; height: 60px; color: #ffffff; background-color: #6c8ff8; border-radius: 10px"
  351. >
  352. 已阅读并同意协议内容
  353. </el-button>
  354. </div>
  355. </template>
  356. </el-dialog>
  357. <el-dialog v-model="privacyShow">
  358. <template #header>
  359. <div>
  360. <p style="display: flex; align-items: center; justify-content: center">《[U店在这] 隐私协议》</p>
  361. </div>
  362. </template>
  363. <div style="height: 50vh; padding: 10px; overflow-y: auto; font-size: 14px; line-height: 1.6">
  364. <p />
  365. <p>生效日期:2024 年 10 月 29 日</p>
  366. <p>
  367. 欢迎使用[U店在这]!我们深知个人信息对您的重要性,并将竭尽全力保护您的隐私。本隐私协议旨在向您说明我们如何收集、使用、存储和保护您的个人信息,以及您对个人信息享有的权利。
  368. </p>
  369. <p>一、定义</p>
  370. <p>
  371. 1.“个人信息”login
  372. 是指以电子或者其他方式记录的能够单独或者与其他信息结合识别特定自然人身份或者反映特定自然人活动情况的各种信息。
  373. </p>
  374. <p>2.“我们”、“本公司” 或 “[U店在这]” 指 [APP 所属公司名称]。</p>
  375. <p>二、信息收集范围</p>
  376. <p>3.您在注册、登录 [U店在这] 时提供的信息,包括但不限于用户名、密码、手机号码、电子邮箱等。</p>
  377. <p>4.您在使用 [U店在这] 过程中产生的信息,如浏览记录、搜索记录、交易记录等。</p>
  378. <p>5.我们从第三方获取的信息,如您通过第三方平台登录 [U店在这] 时,我们可能会获取您在该第三方平台上的部分信息。</p>
  379. <p>三、信息使用目的</p>
  380. <p>6.为您提供个性化的服务和内容,满足您的需求。</p>
  381. <p>7.改善和优化 [U店在这] 的功能和体验。</p>
  382. <p>8.进行数据分析和研究,以提升我们的服务质量和效率。</p>
  383. <p>9.与您进行沟通和互动,回复您的咨询和反馈。</p>
  384. <p>10.遵守法律法规的要求,履行我们的法律义务。</p>
  385. <p>四、信息存储</p>
  386. <p>11.我们将按照法律法规的要求,将您的个人信息存储在安全的服务器上。</p>
  387. <p>12.我们会采取合理的技术和管理措施,确保您的个人信息的安全,防止其被未经授权的访问、披露、使用、修改或丢失。</p>
  388. <p>
  389. 13.我们将根据您的使用情况和需求,确定个人信息的存储期限。在存储期限届满后,我们将对您的个人信息进行删除或匿名化处理。
  390. </p>
  391. <p>五、信息共享与披露</p>
  392. <p>14.我们不会向第三方出售、出租或交易您的个人信息。</p>
  393. <p>
  394. 15.我们可能会在以下情况下与第三方共享您的个人信息: 在获得您的明确同意后;
  395. 为了向您提供特定的服务,我们需要与第三方合作,此时我们会与该第三方签订保密协议,确保其按照本隐私协议的要求处理您的个人信息;
  396. 为了遵守法律法规的要求,履行我们的法律义务。
  397. </p>
  398. <p>
  399. 16.在以下情况下,我们可能会披露您的个人信息: 经您明确同意或授权; 根据法律法规的要求,向有权机关披露;
  400. 为了维护我们的合法权益,如在涉及诉讼、仲裁等法律程序时。
  401. </p>
  402. <p>六、您的权利</p>
  403. <p>17.您有权访问、更正、删除您的个人信息。您可以通过 [U店在这] 的设置页面或联系我们的客服来行使这些权利。</p>
  404. <p>18.您有权撤回您对我们处理您个人信息的同意。但请注意,撤回同意可能会影响您使用 [U店在这] 的部分功能。</p>
  405. <p>19.您有权要求我们提供关于我们处理您个人信息的详细信息,包括处理目的、处理方式、处理的个人信息种类等。</p>
  406. <p>
  407. 20.您有权要求我们限制对您个人信息的处理,例如在您认为我们处理您个人信息的行为违反法律法规的要求或您的合法权益受到侵害时。
  408. </p>
  409. <p>七、Cookie 和同类技术的使用</p>
  410. <p>21.我们可能会使用 Cookie 和同类技术来收集和存储您的信息,以便为您提供更好的服务和体验。</p>
  411. <p>
  412. 22.您可以通过设置浏览器的隐私设置来拒绝接受 Cookie 和同类技术。但请注意,拒绝接受 Cookie和同类技术可能会影响您使用
  413. [U店在这] 的部分功能。
  414. </p>
  415. <p>八、未成年人保护</p>
  416. <p>23.[U店在这] 不面向未成年人提供服务。如果您是未成年人,请不要使用 [U店在这]。</p>
  417. <p>24.如果我们发现我们在未经授权的情况下收集了未成年人的个人信息,我们将立即采取措施删除该信息。</p>
  418. <p>九、隐私政策的变更</p>
  419. <p>25.我们可能会根据法律法规的要求、业务发展的需要或用户反馈等因素,对本隐私协议进行修订。</p>
  420. <p>
  421. 26.修订后的隐私协议将在 [U店在这]上公布,并在公布后的特定时间生效。如果您在修订后的隐私协议生效后继续使用
  422. [U店在这],则视为您同意修订后的隐私协议。
  423. </p>
  424. <p>十、联系方式</p>
  425. <p>如果您对本隐私协议有任何疑问、意见或建议,请通过以下方式与我们联系:</p>
  426. <p>[爱丽恩(大连)贸易有限公司]</p>
  427. <p>[辽宁省大连市中山区普照街44号国泰港汇中心一层F109-110、112-117、FGQ103号商铺]</p>
  428. <p>[0411-81820856]</p>
  429. <p>[15641179898@163.com]</p>
  430. <p>我们将尽快回复您的咨询和反馈。</p>
  431. <p>感谢您对 [U店在这]的信任和支持!</p>
  432. </div>
  433. <template #footer>
  434. <div style="display: flex; align-items: center; justify-content: center; width: 100%">
  435. <el-button
  436. @click="privacyShow = false"
  437. style="width: 406px; height: 60px; color: #ffffff; background-color: #6c8ff8; border-radius: 10px"
  438. >
  439. 已阅读并同意协议内容
  440. </el-button>
  441. </div>
  442. </template>
  443. </el-dialog>
  444. <el-dialog title="忘记密码" v-model="forgetShow" style="height: 600px">
  445. <el-form ref="forgetFormRef" :model="forgetForm" :rules="forgetRules" size="large" class="login-form-content">
  446. <el-form-item prop="phone" label="手机号码" label-position="top">
  447. <el-input v-model="forgetForm.phone" placeholder="请输入手机号码" maxlength="11" clearable />
  448. </el-form-item>
  449. <el-form-item prop="code" label="验证码" label-position="top">
  450. <div class="code-wrapper">
  451. <el-input v-model="forgetForm.code" placeholder="请输入验证码" maxlength="6" clearable class="code-input" />
  452. <el-button :disabled="forgotCodeCountdown > 0" @click="sendForgotCode" class="code-button">
  453. {{ forgotCodeCountdown > 0 ? `${forgotCodeCountdown}秒后重试` : "获取验证码" }}
  454. </el-button>
  455. </div>
  456. </el-form-item>
  457. <div
  458. style="
  459. margin-bottom: 10px;
  460. font-family: Inter;
  461. font-size: 20px;
  462. font-style: normal;
  463. font-weight: 400;
  464. color: #000000;
  465. text-align: left;
  466. text-transform: none;
  467. "
  468. >
  469. 设置新密码
  470. </div>
  471. <el-form-item prop="newPassword" label="新密码" label-position="top">
  472. <el-input
  473. v-model="forgetForm.newPassword"
  474. type="password"
  475. placeholder="请输入新密码"
  476. show-password
  477. autocomplete="new-password"
  478. maxlength="16"
  479. clearable
  480. @input="handlePasswordInput($event, 'forgetForm', 'newPassword')"
  481. />
  482. <div class="password-tip">密码长度为6-16位,必须包含字母和数字,不允许空格和特殊符号</div>
  483. </el-form-item>
  484. </el-form>
  485. <template #footer>
  486. <div style="display: flex; align-items: center; justify-content: center">
  487. <el-button
  488. :loading="loading"
  489. @click="handleForgotPasswordConfirm"
  490. style="width: 406px; height: 60px; color: #ffffff; background-color: #6c8ff8; border-radius: 10px"
  491. >
  492. 确认
  493. </el-button>
  494. </div>
  495. </template>
  496. </el-dialog>
  497. <el-dialog title="注册账号" v-model="registerShow" style="height: 700px">
  498. <el-form ref="registerFormRef" :model="registerForm" :rules="registerRules" size="large" class="login-form-content">
  499. <el-form-item prop="phone" label="手机号码" label-position="top">
  500. <el-input v-model="registerForm.phone" placeholder="请输入手机号码" maxlength="11" clearable />
  501. </el-form-item>
  502. <el-form-item prop="code" label="验证码" label-position="top">
  503. <div class="code-wrapper">
  504. <el-input v-model="registerForm.code" placeholder="请输入验证码" maxlength="6" clearable class="code-input" />
  505. <el-button :disabled="registerCodeCountdown > 0" @click="sendRegisterCode" class="code-button">
  506. {{ registerCodeCountdown > 0 ? `${registerCodeCountdown}秒后重试` : "获取验证码" }}
  507. </el-button>
  508. </div>
  509. </el-form-item>
  510. <el-form-item prop="password" label="密码" label-position="top">
  511. <el-input
  512. v-model="registerForm.password"
  513. type="password"
  514. placeholder="请输入密码"
  515. show-password
  516. autocomplete="new-password"
  517. maxlength="16"
  518. clearable
  519. @input="handlePasswordInput($event, 'registerForm', 'password')"
  520. />
  521. <div class="password-tip">密码长度为6-16位,必须包含字母和数字,不允许空格和特殊符号</div>
  522. </el-form-item>
  523. <el-form-item prop="confirmPassword" label="确认密码" label-position="top">
  524. <el-input
  525. v-model="registerForm.confirmPassword"
  526. type="password"
  527. placeholder="请再次输入密码"
  528. show-password
  529. autocomplete="new-password"
  530. maxlength="16"
  531. clearable
  532. @input="handlePasswordInput($event, 'registerForm', 'confirmPassword')"
  533. />
  534. </el-form-item>
  535. </el-form>
  536. <!-- 用户协议 -->
  537. <div class="agreement-wrapper">
  538. <el-checkbox v-model="registerAgreed" />
  539. <span class="agreement-text">
  540. 我已阅读并同意
  541. <span class="link-text" @click="handleUserAgreement">《用户协议》</span>
  542. <span class="link-text" @click="handlePrivacyPolicy">《隐私政策》</span>
  543. </span>
  544. </div>
  545. <template #footer>
  546. <div style="display: flex; align-items: center; justify-content: center">
  547. <el-button
  548. @click="handleRegister"
  549. style="width: 406px; height: 60px; color: #ffffff; background-color: #6c8ff8; border-radius: 10px"
  550. >
  551. 注册
  552. </el-button>
  553. </div>
  554. </template>
  555. </el-dialog>
  556. </div>
  557. </template>
  558. <script setup lang="ts" name="login">
  559. import { ref, reactive, watch, onMounted, onBeforeUnmount, nextTick } from "vue";
  560. import { useRoute, useRouter } from "vue-router";
  561. import { HOME_URL } from "@/config";
  562. import { Login } from "@/api/interface/index";
  563. import { ElMessage, ElNotification } from "element-plus";
  564. import { loginApi } from "@/api/modules/login";
  565. import { useUserStore } from "@/stores/modules/user";
  566. import { useTabsStore } from "@/stores/modules/tabs";
  567. import { useKeepAliveStore } from "@/stores/modules/keepAlive";
  568. import { initDynamicRouter } from "@/routers/modules/dynamicRouter";
  569. import type { ElForm } from "element-plus";
  570. import md5 from "md5";
  571. import { validatePassword, validateConfirmPassword } from "@/utils/eleValidate";
  572. import {
  573. forgetPassword,
  574. getImgCode,
  575. getPhoneCode,
  576. loginAccount,
  577. registerAccount,
  578. registerCheck
  579. } from "@/api/modules/newLoginApi";
  580. import { getMerchantByPhone } from "@/api/modules/homeEntry";
  581. import { localGet, localRemove, localSet } from "@/utils";
  582. import * as path from "node:path";
  583. import { checkMenuClickPermission } from "@/utils/permission";
  584. import { aiLogin } from "@/api/indexAi";
  585. const router = useRouter();
  586. const route = useRoute();
  587. const userStore = useUserStore();
  588. const tabsStore = useTabsStore();
  589. const keepAliveStore = useKeepAliveStore();
  590. const userShow = ref(false);
  591. const privacyShow = ref(false);
  592. const forgetShow = ref(false);
  593. const registerShow = ref(false);
  594. // 当前激活的标签
  595. const activeTab = ref<"password" | "code">("password");
  596. // 表单引用
  597. const passwordFormRef = ref<InstanceType<typeof ElForm>>();
  598. const codeFormRef = ref<InstanceType<typeof ElForm>>();
  599. const forgetFormRef = ref<InstanceType<typeof ElForm>>();
  600. const registerFormRef = ref<InstanceType<typeof ElForm>>();
  601. // 加载状态
  602. const loading = ref(false);
  603. // 用户协议同意状态
  604. const agreed = ref(false);
  605. // 密码登录表单
  606. const passwordForm = reactive({
  607. phone: "",
  608. password: "",
  609. captcha: ""
  610. });
  611. const forgetForm = ref({
  612. phone: "",
  613. code: "",
  614. newPassword: ""
  615. });
  616. // 注册表单
  617. const registerForm = reactive({
  618. phone: "",
  619. code: "",
  620. password: "",
  621. confirmPassword: ""
  622. });
  623. // 验证码登录表单
  624. const codeForm = reactive({
  625. phone: "",
  626. code: ""
  627. });
  628. // 图片验证码
  629. const captchaImage = ref("");
  630. // 登录验证码倒计时
  631. const loginCodeCountdown = ref(0);
  632. let loginCountdownTimer: NodeJS.Timeout | null = null;
  633. // 忘记密码验证码倒计时
  634. const forgotCodeCountdown = ref(0);
  635. let forgotCountdownTimer: NodeJS.Timeout | null = null;
  636. // 注册验证码倒计时
  637. const registerCodeCountdown = ref(0);
  638. let registerCountdownTimer: NodeJS.Timeout | null = null;
  639. // 表单验证规则
  640. const passwordRules = reactive({
  641. phone: [
  642. { required: true, message: "请输入手机号", trigger: "blur" },
  643. { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号码", trigger: "blur" }
  644. ],
  645. password: [{ required: true, message: "请输入密码", trigger: "blur" }],
  646. captcha: [{ required: true, message: "请输入验证码", trigger: "blur" }]
  647. });
  648. const codeRules = reactive({
  649. phone: [
  650. { required: true, message: "请输入手机号", trigger: "blur" },
  651. { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号码", trigger: "blur" }
  652. ],
  653. code: [{ required: true, message: "请输入验证码", trigger: "blur" }]
  654. });
  655. // 密码验证函数已从 @/utils/eleValidate 导入
  656. const forgetRules = ref({
  657. phone: [
  658. { required: true, message: "请输入手机号", trigger: "blur" },
  659. { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号码", trigger: "blur" }
  660. ],
  661. code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
  662. newPassword: [
  663. { required: true, message: "请输入新密码", trigger: "blur" },
  664. { validator: validatePassword, trigger: "blur" }
  665. ]
  666. });
  667. // 注册表单验证规则
  668. const registerRules = reactive({
  669. phone: [
  670. { required: true, message: "请输入手机号", trigger: "blur" },
  671. { pattern: /^1[3-9]\d{9}$/, message: "请输入正确的手机号码", trigger: "blur" }
  672. ],
  673. code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
  674. password: [
  675. { required: true, message: "请输入密码", trigger: "blur" },
  676. { validator: validatePassword, trigger: "blur" }
  677. ],
  678. confirmPassword: [
  679. { required: true, message: "请确认密码", trigger: "blur" },
  680. {
  681. validator: validateConfirmPassword(() => registerForm.password),
  682. trigger: "blur"
  683. }
  684. ]
  685. });
  686. // 注册协议同意状态
  687. const registerAgreed = ref(false);
  688. // 处理密码输入,过滤特殊符号和空格,限制16位
  689. const handlePasswordInput = (value: string, formName: string, fieldName: string) => {
  690. // 只保留字母和数字,移除特殊符号和空格
  691. const filteredValue = value.replace(/[^a-zA-Z0-9]/g, "");
  692. // 限制最大长度为16(maxlength 已经限制,这里再确保一下)
  693. const finalValue = filteredValue.slice(0, 16);
  694. // 更新对应的表单字段
  695. if (formName === "passwordForm") {
  696. passwordForm.password = finalValue;
  697. } else if (formName === "forgetForm") {
  698. forgetForm.value.newPassword = finalValue;
  699. } else if (formName === "registerForm") {
  700. if (fieldName === "password") {
  701. registerForm.password = finalValue;
  702. } else if (fieldName === "confirmPassword") {
  703. registerForm.confirmPassword = finalValue;
  704. }
  705. }
  706. };
  707. // 刷新图片验证码
  708. const refreshCaptcha = async () => {
  709. captchaImage.value = "";
  710. let res: any = await getImgCode();
  711. const blobUrl = URL.createObjectURL(res);
  712. captchaImage.value = blobUrl;
  713. };
  714. // 发送忘记密码验证码
  715. const sendForgotCode = async () => {
  716. if (!forgetForm.value.phone) {
  717. ElMessage.warning("请先输入手机号");
  718. return;
  719. }
  720. if (!/^1[3-9]\d{9}$/.test(forgetForm.value.phone)) {
  721. ElMessage.warning("请输入正确的手机号码");
  722. return;
  723. }
  724. let phoneCode: any = await getPhoneCode({ phone: forgetForm.value.phone, appType: "2", businessType: "6" });
  725. if (phoneCode.code === 200) {
  726. ElMessage.success("验证码已发送");
  727. // 清除之前的定时器
  728. if (forgotCountdownTimer) {
  729. clearInterval(forgotCountdownTimer);
  730. forgotCountdownTimer = null;
  731. }
  732. // 开始倒计时
  733. forgotCodeCountdown.value = 60;
  734. forgotCountdownTimer = setInterval(() => {
  735. forgotCodeCountdown.value--;
  736. if (forgotCodeCountdown.value <= 0) {
  737. if (forgotCountdownTimer) {
  738. clearInterval(forgotCountdownTimer);
  739. forgotCountdownTimer = null;
  740. }
  741. }
  742. }, 1000);
  743. }
  744. };
  745. // 发送短信验证码
  746. const sendLoginCode = async () => {
  747. if (!codeForm.phone) {
  748. ElMessage.warning("请先输入手机号");
  749. return;
  750. }
  751. if (!/^1[3-9]\d{9}$/.test(codeForm.phone)) {
  752. ElMessage.warning("请输入正确的手机号码");
  753. return;
  754. }
  755. let phoneCode: any = await getPhoneCode({ phone: codeForm.phone, appType: "2", businessType: "0" });
  756. if (phoneCode.code === 200) {
  757. ElMessage.success("验证码已发送");
  758. // 清除之前的定时器
  759. if (loginCountdownTimer) {
  760. clearInterval(loginCountdownTimer);
  761. loginCountdownTimer = null;
  762. }
  763. // 开始倒计时
  764. loginCodeCountdown.value = 60;
  765. loginCountdownTimer = setInterval(() => {
  766. loginCodeCountdown.value--;
  767. if (loginCodeCountdown.value <= 0) {
  768. if (loginCountdownTimer) {
  769. clearInterval(loginCountdownTimer);
  770. loginCountdownTimer = null;
  771. }
  772. }
  773. }, 1000);
  774. }
  775. };
  776. // 发送注册验证码
  777. const sendRegisterCode = async () => {
  778. if (!registerForm.phone) {
  779. ElMessage.warning("请先输入手机号");
  780. return;
  781. }
  782. if (!/^1[3-9]\d{9}$/.test(registerForm.phone)) {
  783. ElMessage.warning("请输入正确的手机号码");
  784. return;
  785. }
  786. let checkRes = await registerCheck({ phone: registerForm.phone });
  787. console.log(checkRes.data, "checkRes.data");
  788. if (!checkRes.data) {
  789. ElMessage.warning("该账号已注册");
  790. return;
  791. }
  792. let phoneCode: any = await getPhoneCode({ phone: registerForm.phone, appType: "2", businessType: "2" });
  793. if (phoneCode.code === 200) {
  794. console.log(phoneCode, "phoneCode");
  795. ElMessage.success("验证码已发送");
  796. // 清除之前的定时器
  797. if (registerCountdownTimer) {
  798. clearInterval(registerCountdownTimer);
  799. registerCountdownTimer = null;
  800. }
  801. // 开始倒计时
  802. registerCodeCountdown.value = 60;
  803. registerCountdownTimer = setInterval(() => {
  804. registerCodeCountdown.value--;
  805. if (registerCodeCountdown.value <= 0) {
  806. if (registerCountdownTimer) {
  807. clearInterval(registerCountdownTimer);
  808. registerCountdownTimer = null;
  809. }
  810. }
  811. }, 1000);
  812. }
  813. };
  814. // 登录处理
  815. const handleLogin = async () => {
  816. if (!agreed.value) {
  817. ElMessage.warning("请先同意用户协议和隐私政策");
  818. return;
  819. }
  820. let formRef: InstanceType<typeof ElForm> | undefined;
  821. let formData: any = {};
  822. if (activeTab.value === "password") {
  823. formRef = passwordFormRef.value;
  824. formData = {
  825. phone: passwordForm.phone,
  826. password: passwordForm.password,
  827. captcha: passwordForm.captcha,
  828. // code:'',
  829. isPassword: true
  830. };
  831. } else {
  832. formRef = codeFormRef.value;
  833. formData = {
  834. phone: codeForm.phone,
  835. code: codeForm.code,
  836. isPassword: false
  837. };
  838. }
  839. console.log("test-git");
  840. if (!formRef) return;
  841. await formRef.validate(async valid => {
  842. if (!valid) return;
  843. loading.value = true;
  844. try {
  845. let loginParams: any;
  846. const res: any = await loginAccount(formData);
  847. if (res.data) {
  848. userStore.setToken(res.data.token);
  849. userStore.setUserInfo(res.data);
  850. const userInfo = {
  851. userInfo: res.data,
  852. token: res.data.token
  853. };
  854. localSet("geeker-user", userInfo);
  855. if (localGet("geeker-user").userInfo.storeId) {
  856. localSet("createdId", localGet("geeker-user").userInfo.storeId);
  857. } else {
  858. localRemove("createdId");
  859. }
  860. if (localGet("geeker-user").userInfo.phone) {
  861. localSet("iphone", localGet("geeker-user").userInfo.phone);
  862. } else {
  863. localRemove("iphone");
  864. }
  865. if (localGet("geeker-user").userInfo.businessSection) {
  866. localSet("businessSection", localGet("geeker-user").userInfo.businessSection);
  867. } else {
  868. localRemove("businessSection");
  869. }
  870. if (localGet("geeker-user").userInfo.mealsFlag) {
  871. localSet("mealsFlag", localGet("geeker-user").userInfo.mealsFlag);
  872. } else {
  873. localRemove("mealsFlag");
  874. }
  875. // 旧登录接口成功后,调用AI登录接口
  876. // AI登录失败不影响主系统登录流程,静默处理
  877. try {
  878. await aiLogin();
  879. } catch (error) {
  880. console.error("AI服务登录失败:", error);
  881. }
  882. // 登录成功后,立即获取完整的用户信息(包括头像)
  883. if (res.data.phone) {
  884. try {
  885. const merchantRes: any = await getMerchantByPhone({ phone: res.data.phone });
  886. if (merchantRes && merchantRes.code == 200 && merchantRes.data) {
  887. // 更新用户信息到本地存储,确保保留businessSection字段
  888. const updatedUserInfo = {
  889. ...userInfo,
  890. userInfo: {
  891. ...userInfo.userInfo,
  892. ...merchantRes.data,
  893. // 确保保留businessSection字段
  894. businessSection: userInfo.userInfo.businessSection
  895. }
  896. };
  897. localSet("geeker-user", updatedUserInfo);
  898. userStore.setUserInfo(updatedUserInfo.userInfo);
  899. // 如果有头像,缓存头像URL
  900. const avatarUrl = merchantRes.data.headImg || merchantRes.data.avatar || "";
  901. if (avatarUrl && avatarUrl.trim() !== "") {
  902. localSet(`avatar_${res.data.phone}`, avatarUrl);
  903. }
  904. }
  905. } catch (error) {
  906. console.error("获取用户信息失败:", error);
  907. // 即使获取失败,也不影响登录流程
  908. }
  909. }
  910. sessionStorage.removeItem("ticketManagement_activeName");
  911. await initDynamicRouter();
  912. // 3.清空 tabs、keepAlive 数据
  913. await tabsStore.setTabs([]);
  914. await keepAliveStore.setKeepAliveName([]);
  915. // 4.等待路由完全初始化后再跳转
  916. // 使用 nextTick 确保路由已完全添加
  917. await nextTick();
  918. const { contractManagement, foodBusinessLicense, entertainmentBusinessLicense } = await checkMenuClickPermission();
  919. // 登录后根据不同证照权限跳转
  920. if ((contractManagement && foodBusinessLicense && entertainmentBusinessLicense) || contractManagement) {
  921. // 合同管理权限优先
  922. router.replace("/licenseManagement/contractManagement");
  923. } else if (foodBusinessLicense) {
  924. // 食品经营许可证权限
  925. router.replace("/licenseManagement/foodBusinessLicense");
  926. } else if (entertainmentBusinessLicense) {
  927. // 娱乐经营许可证权限
  928. router.replace("/licenseManagement/entertainmentLicense");
  929. } else {
  930. // 5.跳转到首页,使用 replace 避免历史记录问题
  931. await router.replace(HOME_URL);
  932. }
  933. ElNotification({
  934. title: "登录成功",
  935. type: "success",
  936. duration: 3000
  937. });
  938. } else {
  939. ElNotification({
  940. title: "登录失败,请检查账号和密码",
  941. type: "error",
  942. duration: 3000
  943. });
  944. }
  945. } catch (error: any) {
  946. ElNotification({
  947. title: error?.msg || "登录失败,请稍后重试",
  948. type: "error",
  949. duration: 3000
  950. });
  951. } finally {
  952. loading.value = false;
  953. }
  954. });
  955. };
  956. // 忘记密码
  957. const handleForgotPassword = () => {
  958. forgetShow.value = true;
  959. };
  960. // 忘记密码提交
  961. const handleForgotPasswordConfirm = async () => {
  962. if (!forgetFormRef.value) return;
  963. await forgetFormRef.value.validate(async valid => {
  964. if (!valid) return;
  965. loading.value = true;
  966. try {
  967. let resForget: any = await forgetPassword({
  968. phone: forgetForm.value.phone,
  969. verificationCode: forgetForm.value.code,
  970. type: "0",
  971. newPassword: forgetForm.value.newPassword
  972. });
  973. if (resForget.code === 200) {
  974. ElNotification({
  975. title: "修改成功",
  976. type: "success",
  977. duration: 3000
  978. });
  979. forgetShow.value = false;
  980. // 重置表单
  981. if (forgetFormRef.value) {
  982. forgetFormRef.value.resetFields();
  983. }
  984. } else {
  985. ElNotification({
  986. title: resForget.msg || "修改失败,请稍后重试",
  987. type: "error",
  988. duration: 3000
  989. });
  990. }
  991. } catch (error: any) {
  992. ElNotification({
  993. title: error?.msg || "修改失败,请稍后重试",
  994. type: "error",
  995. duration: 3000
  996. });
  997. } finally {
  998. loading.value = false;
  999. }
  1000. });
  1001. };
  1002. // 打开注册弹窗
  1003. const openRegister = () => {
  1004. registerShow.value = true;
  1005. };
  1006. // 注册提交
  1007. const handleRegister = async () => {
  1008. if (!registerAgreed.value) {
  1009. ElMessage.warning("请先同意用户协议和隐私政策");
  1010. return;
  1011. }
  1012. if (!registerFormRef.value) return;
  1013. await registerFormRef.value.validate(async valid => {
  1014. if (!valid) return;
  1015. loading.value = true;
  1016. try {
  1017. // TODO: 调用注册接口
  1018. const registerParams = {
  1019. phone: registerForm.phone,
  1020. code: registerForm.code,
  1021. // password: md5(registerForm.password)
  1022. password: registerForm.password
  1023. };
  1024. let res: any = await registerAccount(registerParams);
  1025. if (res.code === 200) {
  1026. ElNotification({
  1027. title: "注册成功",
  1028. type: "success",
  1029. duration: 3000
  1030. });
  1031. // 注册成功后关闭弹窗
  1032. registerShow.value = false;
  1033. // 重置表单
  1034. if (registerFormRef.value) {
  1035. registerFormRef.value.resetFields();
  1036. }
  1037. registerAgreed.value = false;
  1038. }
  1039. } catch (error: any) {
  1040. ElNotification({
  1041. title: error?.msg || "注册失败,请稍后重试",
  1042. type: "error",
  1043. duration: 3000
  1044. });
  1045. } finally {
  1046. loading.value = false;
  1047. }
  1048. });
  1049. };
  1050. // 用户协议
  1051. const handleUserAgreement = () => {
  1052. ElMessage.info("用户协议");
  1053. userShow.value = true;
  1054. };
  1055. // 隐私政策
  1056. const handlePrivacyPolicy = () => {
  1057. ElMessage.info("隐私政策");
  1058. privacyShow.value = true;
  1059. };
  1060. // 切换登录方式时重置表单
  1061. const resetForms = () => {
  1062. if (passwordFormRef.value) {
  1063. passwordFormRef.value.resetFields();
  1064. }
  1065. if (codeFormRef.value) {
  1066. codeFormRef.value.resetFields();
  1067. }
  1068. captchaImage.value = "";
  1069. // 重置登录验证码倒计时
  1070. loginCodeCountdown.value = 0;
  1071. if (loginCountdownTimer) {
  1072. clearInterval(loginCountdownTimer);
  1073. loginCountdownTimer = null;
  1074. }
  1075. };
  1076. // 监听标签切换
  1077. watch(
  1078. () => activeTab.value,
  1079. () => {
  1080. resetForms();
  1081. }
  1082. );
  1083. // 监听注册表单密码变化,重新验证确认密码
  1084. watch(
  1085. () => registerForm.password,
  1086. () => {
  1087. if (registerFormRef.value && registerForm.confirmPassword) {
  1088. registerFormRef.value.validateField("confirmPassword");
  1089. }
  1090. }
  1091. );
  1092. onMounted(() => {
  1093. if (activeTab.value === "password") {
  1094. refreshCaptcha();
  1095. }
  1096. document.onkeydown = (e: KeyboardEvent) => {
  1097. if (e.code === "Enter" || e.code === "enter" || e.code === "NumpadEnter") {
  1098. if (loading.value) return;
  1099. handleLogin();
  1100. }
  1101. };
  1102. });
  1103. onBeforeUnmount(() => {
  1104. document.onkeydown = null;
  1105. // 清理所有定时器
  1106. if (loginCountdownTimer) {
  1107. clearInterval(loginCountdownTimer);
  1108. loginCountdownTimer = null;
  1109. }
  1110. if (forgotCountdownTimer) {
  1111. clearInterval(forgotCountdownTimer);
  1112. forgotCountdownTimer = null;
  1113. }
  1114. if (registerCountdownTimer) {
  1115. clearInterval(registerCountdownTimer);
  1116. registerCountdownTimer = null;
  1117. }
  1118. });
  1119. </script>
  1120. <style scoped lang="scss">
  1121. .login-bg {
  1122. position: relative;
  1123. width: 100%;
  1124. height: 100%;
  1125. background: url("../../assets/login/bg.jpg");
  1126. background-repeat: no-repeat;
  1127. background-position: center;
  1128. background-size: cover;
  1129. }
  1130. .left-box {
  1131. .left-box-title {
  1132. position: absolute;
  1133. top: 201px;
  1134. left: 290px;
  1135. display: flex;
  1136. flex-flow: column;
  1137. justify-content: center;
  1138. .left-box-title-text {
  1139. margin-left: 10px;
  1140. font-family: Inter;
  1141. font-size: 30px;
  1142. font-weight: 700;
  1143. line-height: 100%;
  1144. }
  1145. .left-box-title-bottom-text {
  1146. margin-top: 20px;
  1147. font-family: Inter;
  1148. font-size: 36px;
  1149. font-weight: 300;
  1150. line-height: 100%;
  1151. color: #8b8b8b;
  1152. }
  1153. }
  1154. .login-class {
  1155. position: absolute;
  1156. top: 336px;
  1157. left: 328px;
  1158. width: 686px;
  1159. height: 585px;
  1160. }
  1161. .qrcode-box {
  1162. position: absolute;
  1163. top: 626px;
  1164. left: 282px;
  1165. display: flex;
  1166. flex-flow: column;
  1167. align-items: center;
  1168. justify-content: center;
  1169. width: 178px;
  1170. height: 238px;
  1171. background-color: #ffffff;
  1172. border-radius: 20px;
  1173. .qrcode-box-text {
  1174. margin-top: 10px;
  1175. font-family: Inter;
  1176. font-size: 16px;
  1177. font-weight: 400;
  1178. line-height: 100%;
  1179. color: #979797;
  1180. text-align: center;
  1181. }
  1182. }
  1183. }
  1184. .right-box {
  1185. position: absolute;
  1186. top: 154px;
  1187. left: 1071px;
  1188. box-sizing: border-box;
  1189. display: flex;
  1190. flex-direction: column;
  1191. width: 546px;
  1192. height: 759px;
  1193. padding: 50px 40px;
  1194. background-color: #ffffff;
  1195. border-radius: 20px;
  1196. box-shadow: 0 4px 20px 0 rgb(0 0 0 / 8%);
  1197. .right-box-title {
  1198. display: flex;
  1199. gap: 8px;
  1200. align-items: center;
  1201. justify-content: center;
  1202. margin-bottom: 30px;
  1203. font-family: Inter;
  1204. font-size: 26px;
  1205. font-style: normal;
  1206. font-weight: 400;
  1207. color: #151515;
  1208. text-align: center;
  1209. text-transform: none;
  1210. }
  1211. .login-tabs {
  1212. :deep(.el-tabs__header) {
  1213. width: fit-content;
  1214. margin: 0 auto;
  1215. }
  1216. :deep(.el-tabs__content) {
  1217. padding-top: 38px;
  1218. }
  1219. :deep(.el-tabs__nav-wrap::after) {
  1220. background-color: #e4e7ed;
  1221. }
  1222. :deep(.el-tabs__item) {
  1223. padding: 12px 24px;
  1224. font-size: 16px;
  1225. color: #606266;
  1226. &.is-active {
  1227. font-weight: 700;
  1228. color: #000000;
  1229. }
  1230. &:hover {
  1231. color: #606266;
  1232. }
  1233. }
  1234. :deep(.el-tabs__active-bar) {
  1235. background-color: #6c8ff8;
  1236. }
  1237. }
  1238. .login-button {
  1239. width: 100%;
  1240. height: 44px;
  1241. margin-top: 10px;
  1242. font-size: 16px;
  1243. }
  1244. :deep(.login-button.el-button--primary) {
  1245. background-color: #6c8ff8;
  1246. border-color: #6c8ff8;
  1247. &:hover {
  1248. background-color: #5a7de8;
  1249. border-color: #5a7de8;
  1250. }
  1251. &:active {
  1252. background-color: #4a6dd8;
  1253. border-color: #4a6dd8;
  1254. }
  1255. }
  1256. .form-footer {
  1257. display: flex;
  1258. justify-content: space-between;
  1259. margin-top: 20px;
  1260. font-size: 14px;
  1261. .link-text {
  1262. color: #409eff;
  1263. cursor: pointer;
  1264. &:hover {
  1265. text-decoration: underline;
  1266. }
  1267. }
  1268. }
  1269. }
  1270. .login-form-content {
  1271. :deep(.el-form-item) {
  1272. margin-bottom: 20px;
  1273. }
  1274. :deep(.el-form-item__label) {
  1275. height: 19px;
  1276. margin-bottom: 10px;
  1277. font-family: Inter;
  1278. font-size: 16px;
  1279. font-style: normal;
  1280. font-weight: 400;
  1281. color: #000000;
  1282. text-align: left;
  1283. text-transform: none;
  1284. &::before {
  1285. display: none;
  1286. }
  1287. }
  1288. :deep(.el-input__wrapper) {
  1289. padding: 12px 15px;
  1290. border: 1px solid #d0d3d6;
  1291. border-radius: 10px;
  1292. }
  1293. .password-tip {
  1294. margin-top: 8px;
  1295. font-size: 12px;
  1296. line-height: 1.5;
  1297. color: #909399;
  1298. }
  1299. }
  1300. .captcha-wrapper {
  1301. display: flex;
  1302. gap: 10px;
  1303. width: 100%;
  1304. .captcha-input {
  1305. flex: 1;
  1306. }
  1307. .captcha-image {
  1308. display: flex;
  1309. flex-shrink: 0;
  1310. align-items: center;
  1311. justify-content: center;
  1312. width: 120px;
  1313. height: 40px;
  1314. cursor: pointer;
  1315. background-color: #f5f7fa;
  1316. border: 1px solid #dcdfe6;
  1317. border-radius: 4px;
  1318. img {
  1319. width: 100%;
  1320. height: 100%;
  1321. object-fit: contain;
  1322. }
  1323. .captcha-placeholder {
  1324. font-size: 12px;
  1325. color: #909399;
  1326. }
  1327. }
  1328. }
  1329. .code-wrapper {
  1330. display: flex;
  1331. gap: 10px;
  1332. align-items: center;
  1333. width: 100%;
  1334. .code-input {
  1335. flex: 1;
  1336. }
  1337. .code-button {
  1338. flex-shrink: 0;
  1339. white-space: nowrap;
  1340. }
  1341. }
  1342. .agreement-wrapper {
  1343. display: flex;
  1344. align-items: center;
  1345. // justify-content: center;
  1346. margin-top: 20px;
  1347. font-size: 14px;
  1348. color: #606266;
  1349. :deep(.el-checkbox) {
  1350. margin-right: 8px;
  1351. }
  1352. .agreement-text {
  1353. .link-text {
  1354. color: #409eff;
  1355. cursor: pointer;
  1356. &:hover {
  1357. text-decoration: underline;
  1358. }
  1359. }
  1360. }
  1361. }
  1362. </style>