estimation.vue 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. <template>
  2. <Theme>
  3. <view class="wrap">
  4. <Navbar title="运费估算" fixed border> </Navbar>
  5. <view class="container">
  6. <view class="card">
  7. <view class="field">
  8. <trans class="label required" _t="从VAVABUY到"></trans>
  9. <UPickerField
  10. v-model="form.country"
  11. :columns="cityOptions"
  12. labelKey="name"
  13. valueKey="id"
  14. @change="onCityChange"
  15. />
  16. </view>
  17. <!-- 重量 -->
  18. <view class="field">
  19. <view class="label required">
  20. <trans _t="重量(克)" />
  21. </view>
  22. <Search
  23. class="input"
  24. type="number"
  25. border="none"
  26. inputAlign="right"
  27. :placeholder="t('请输入重量')"
  28. v-model.trim="form.weight"
  29. />
  30. </view>
  31. <!-- 商品类别 折叠 -->
  32. <view class="collapse">
  33. <view class="collapse-head" @tap="toggleCate">
  34. <trans _t="商品类别" />
  35. <MultiSelectDropdown
  36. v-model="selectedIds"
  37. :placeholder="t('请选择商品类别')"
  38. :options="categoryOptions"
  39. @change="handleChange"
  40. />
  41. </view>
  42. </view>
  43. <!-- 体积输入 -->
  44. <view class="dim-list">
  45. <view class="dim-item" v-for="item in dims" :key="item.key">
  46. <view class="dim-label">{{ t(item.label) }}</view>
  47. <view class="dim-input-wrap">
  48. <Search
  49. class="dim-input"
  50. type="number"
  51. border="none"
  52. inputAlign="right"
  53. v-model.trim="form[item.key]"
  54. />
  55. <text class="unit">cm</text>
  56. </view>
  57. </view>
  58. </view>
  59. </view>
  60. <!-- 底部按钮 -->
  61. <view class="footer">
  62. <button class="btn" type="default" @tap="submit">
  63. <trans _t="查询" />
  64. </button>
  65. </view>
  66. </view>
  67. <view class="result">
  68. <view class="list" v-for="(item, index) in detail" :key="index">
  69. <logisticsList :item="item" />
  70. </view>
  71. </view>
  72. </view>
  73. </Theme>
  74. </template>
  75. <script setup>
  76. import { reactive, ref, nextTick } from "vue";
  77. import Navbar from "@/components/navbar";
  78. import navMenu from "@/components/nav_menu";
  79. import Search from "@/components/input";
  80. import UPickerField from "@/components/UPickerField.vue";
  81. import MultiSelectDropdown from "@/components/MultiSelectDropdown.vue";
  82. import logisticsList from "./components/logistics_list";
  83. import { t } from "@/locale";
  84. import { SHOP_EXPRESS_CONFIG, SHOP_POST_CALCULATE } from "@/api";
  85. import { onShow } from "@dcloudio/uni-app";
  86. import { Toast } from "@/utils";
  87. const detail = ref([]);
  88. const form = reactive({
  89. weight: "", // g
  90. length: "", // cm
  91. width: "",
  92. height: "",
  93. category: "",
  94. country: "",
  95. });
  96. const dims = [
  97. { key: "length", label: "长度" },
  98. { key: "width", label: "宽度" },
  99. { key: "height", label: "高度" },
  100. ];
  101. const cityOptions = ref([]);
  102. const onCityChange = (e) => {
  103. console.log("change:", e);
  104. };
  105. const categoryOptions = ref([]);
  106. // 选中的ID
  107. const selectedIds = ref([]);
  108. const handleChange = (data) => {
  109. form.category = data.ids.join(",");
  110. };
  111. const submit = async () => {
  112. if (!form.country || !form.weight) {
  113. return Toast(t("请填写必填信息"));
  114. }
  115. try {
  116. detail.value = [];
  117. const res = await SHOP_POST_CALCULATE(form);
  118. detail.value = res.data;
  119. } catch (error) {
  120. Toast(error.msg);
  121. }
  122. };
  123. const getConfig = async () => {
  124. try {
  125. const res = await SHOP_EXPRESS_CONFIG();
  126. categoryOptions.value = res.data.category || [];
  127. cityOptions.value = res.data.counrty || [];
  128. } catch (error) {
  129. Toast(error.msg);
  130. }
  131. };
  132. onShow(() => {
  133. nextTick(() => {
  134. getConfig();
  135. });
  136. });
  137. </script>
  138. <style lang="less" scoped>
  139. @import url("@/style.less");
  140. .wrap {
  141. min-height: 100vh;
  142. background-color: var(--bg);
  143. .container {
  144. padding: 24rpx;
  145. .field {
  146. min-height: 100rpx;
  147. display: flex;
  148. justify-content: space-between;
  149. border: 1rpx solid var(--borderColor);
  150. border-radius: 18rpx;
  151. padding: 16rpx 24rpx;
  152. align-items: center;
  153. margin-bottom: 24rpx;
  154. .label {
  155. color: var(--black);
  156. font-weight: 600;
  157. .size(26rpx);
  158. &.required::before {
  159. content: "*";
  160. color: #ff4d4f;
  161. font-weight: 600;
  162. .size(18rpx);
  163. margin-right: 4rpx;
  164. }
  165. &.to {
  166. justify-self: center;
  167. color: var(--bor-color1);
  168. font-weight: 500;
  169. }
  170. }
  171. .picker {
  172. display: flex;
  173. align-items: center;
  174. justify-content: space-between;
  175. gap: 8rpx;
  176. padding: 6rpx 0;
  177. &.picker-full {
  178. width: 100%;
  179. }
  180. }
  181. .primary {
  182. color: #0ea5e9;
  183. font-weight: 700;
  184. }
  185. .muted {
  186. color: var(--black);
  187. }
  188. .arrow {
  189. color: var(--black);
  190. font-weight: 600;
  191. .size(32rpx);
  192. transform: translateY(-1rpx);
  193. &.open {
  194. transform: rotate(180deg) translateY(1rpx);
  195. }
  196. &.small {
  197. font-size: 16rpx;
  198. }
  199. }
  200. .input {
  201. background: transparent;
  202. }
  203. }
  204. /* 折叠块 */
  205. .collapse {
  206. border: 1px solid var(--borderColor);
  207. border-radius: 12px;
  208. margin-bottom: 12px;
  209. .collapse-head {
  210. display: flex;
  211. align-items: center;
  212. justify-content: space-between;
  213. padding: 12px;
  214. color: var(--black);
  215. font-weight: 600;
  216. .size(26rpx);
  217. }
  218. .collapse-body {
  219. padding: 0 12px 12px;
  220. }
  221. }
  222. /* 尺寸输入 */
  223. .dim-list {
  224. background: #fff;
  225. border-radius: 12px;
  226. overflow: hidden;
  227. border: 1px solid var(--borderColor);
  228. .dim-item {
  229. display: flex;
  230. align-items: center;
  231. justify-content: space-between;
  232. padding: 14px 12px;
  233. border-bottom: 1px solid var(--borderColor);
  234. &:last-child {
  235. border-bottom: 0;
  236. }
  237. }
  238. .dim-label {
  239. .size(24rpx);
  240. font-weight: 600;
  241. color: var(--black);
  242. }
  243. .dim-input-wrap {
  244. display: flex;
  245. align-items: center;
  246. gap: 16rpx;
  247. .dim-input {
  248. min-width: 120rpx;
  249. background: transparent;
  250. }
  251. .unit {
  252. color: var(--bor-color1);
  253. }
  254. }
  255. }
  256. /* 底部按钮 */
  257. .footer {
  258. position: sticky;
  259. bottom: 0;
  260. margin-top: 16px;
  261. padding: 10px 0 6px;
  262. background: linear-gradient(
  263. to bottom,
  264. rgba(255, 255, 255, 0),
  265. #fff 30%,
  266. #fff
  267. );
  268. .btn {
  269. width: 100%;
  270. height: 44px;
  271. line-height: 44px;
  272. border-radius: 10px;
  273. color: #fff;
  274. background: var(--black);
  275. font-weight: 700;
  276. border: none;
  277. &:active {
  278. opacity: 0.9;
  279. }
  280. }
  281. }
  282. }
  283. .result {
  284. padding: 24rpx;
  285. .list {
  286. width: 100%;
  287. }
  288. }
  289. }
  290. </style>