forked from gxwebsoft/mp-10550
修复已知问题
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
export const ENV_CONFIG = {
|
export const ENV_CONFIG = {
|
||||||
// 开发环境
|
// 开发环境
|
||||||
development: {
|
development: {
|
||||||
API_BASE_URL: 'http://127.0.0.1:9200/api',
|
API_BASE_URL: 'https://cms-api.websoft.top/api',
|
||||||
APP_NAME: '时里院子市集',
|
APP_NAME: '时里院子市集',
|
||||||
DEBUG: 'true',
|
DEBUG: 'true',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ import type { CmsDesign, CmsDesignParam } from './model';
|
|||||||
export async function pageCmsDesign(params: CmsDesignParam) {
|
export async function pageCmsDesign(params: CmsDesignParam) {
|
||||||
const res = await request.get<ApiResult<PageResult<CmsDesign>>>(
|
const res = await request.get<ApiResult<PageResult<CmsDesign>>>(
|
||||||
'/cms/cms-design/page',
|
'/cms/cms-design/page',
|
||||||
{
|
params
|
||||||
params
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
return res.data;
|
return res.data;
|
||||||
@@ -25,9 +23,7 @@ export async function pageCmsDesign(params: CmsDesignParam) {
|
|||||||
export async function listCmsDesign(params?: CmsDesignParam) {
|
export async function listCmsDesign(params?: CmsDesignParam) {
|
||||||
const res = await request.get<ApiResult<CmsDesign[]>>(
|
const res = await request.get<ApiResult<CmsDesign[]>>(
|
||||||
'/cms/cms-design',
|
'/cms/cms-design',
|
||||||
{
|
params
|
||||||
params
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
return res.data;
|
return res.data;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type {PageParam} from '@/api/index';
|
import type {PageParam} from '@/api/index';
|
||||||
import type {CmsDesign} from "@/api/cms/cmsDesign/model";
|
import type {CmsDesign} from "@/api/cms/cmsDesign/model";
|
||||||
import type {CmsModel} from "@/api/cms/cmsModel/model";
|
import type {CmsModel} from "@/api/cms/cmsModel/model";
|
||||||
|
import {CmsArticle} from "@/api/cms/cmsArticle/model";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 网站导航记录表
|
* 网站导航记录表
|
||||||
@@ -100,6 +101,8 @@ export interface CmsNavigation {
|
|||||||
label?: string;
|
label?: string;
|
||||||
// 值
|
// 值
|
||||||
value?: number;
|
value?: number;
|
||||||
|
// 文章列表
|
||||||
|
articles?: CmsArticle[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -120,9 +120,7 @@ export async function undeleteWebsiteField(id?: number) {
|
|||||||
export async function configWebsiteField(params?: CmsWebsiteFieldParam) {
|
export async function configWebsiteField(params?: CmsWebsiteFieldParam) {
|
||||||
const res = await request.get<ApiResult<Config>>(
|
const res = await request.get<ApiResult<Config>>(
|
||||||
'/cms/cms-website-field/config',
|
'/cms/cms-website-field/config',
|
||||||
{
|
params
|
||||||
params
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
return res.data;
|
return res.data;
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export interface Config {
|
|||||||
loginBgImg?: string;
|
loginBgImg?: string;
|
||||||
address?: string;
|
address?: string;
|
||||||
tel?: string;
|
tel?: string;
|
||||||
|
workDay?: string;
|
||||||
kefu2?: string;
|
kefu2?: string;
|
||||||
kefu1?: string;
|
kefu1?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export async function uploadOssByPath(filePath: string) {
|
|||||||
let stsExpired = Taro.getStorageSync('stsExpiredAt');
|
let stsExpired = Taro.getStorageSync('stsExpiredAt');
|
||||||
if (!sts || (stsExpired && dayjs().isBefore(dayjs(stsExpired)))) {
|
if (!sts || (stsExpired && dayjs().isBefore(dayjs(stsExpired)))) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const {data: {data: {credentials}}} = await request.get(`https://server.gxwebsoft.com/api/oss/getSTSToken`)
|
const {data: {data: {credentials}}} = await request.get(`https://server.websoft.top/api/oss/getSTSToken`)
|
||||||
Taro.setStorageSync('sts', credentials)
|
Taro.setStorageSync('sts', credentials)
|
||||||
Taro.setStorageSync('stsExpiredAt', credentials.expiration)
|
Taro.setStorageSync('stsExpiredAt', credentials.expiration)
|
||||||
sts = credentials
|
sts = credentials
|
||||||
@@ -66,7 +66,7 @@ export async function uploadFile() {
|
|||||||
const tempFilePath = res.tempFilePaths[0];
|
const tempFilePath = res.tempFilePaths[0];
|
||||||
// 上传图片到OSS
|
// 上传图片到OSS
|
||||||
Taro.uploadFile({
|
Taro.uploadFile({
|
||||||
url: 'https://server.gxwebsoft.com/api/oss/upload',
|
url: 'https://server.websoft.top/api/oss/upload',
|
||||||
filePath: tempFilePath,
|
filePath: tempFilePath,
|
||||||
name: 'file',
|
name: 'file',
|
||||||
header: {
|
header: {
|
||||||
|
|||||||
@@ -46,7 +46,8 @@ export default defineAppConfig({
|
|||||||
'orderDetail/index',
|
'orderDetail/index',
|
||||||
'goodsDetail/index',
|
'goodsDetail/index',
|
||||||
'orderConfirm/index',
|
'orderConfirm/index',
|
||||||
'orderConfirmCart/index'
|
'orderConfirmCart/index',
|
||||||
|
'search/index'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ function Detail() {
|
|||||||
<div className={'bg-white'}>
|
<div className={'bg-white'}>
|
||||||
<div className={'p-4 font-bold text-lg'}>{item?.title}</div>
|
<div className={'p-4 font-bold text-lg'}>{item?.title}</div>
|
||||||
<div className={'text-gray-400 text-sm px-4 '}>{item?.createTime}</div>
|
<div className={'text-gray-400 text-sm px-4 '}>{item?.createTime}</div>
|
||||||
<View className={'content px-3'}>
|
<View className={'content p-4'}>
|
||||||
<RichText nodes={item?.content}/>
|
<RichText nodes={item?.content}/>
|
||||||
</View>
|
</View>
|
||||||
<Line height={44}/>
|
<Line height={44}/>
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ function PayRecord() {
|
|||||||
|
|
||||||
const reloadMore = async () => {
|
const reloadMore = async () => {
|
||||||
setPage(page + 1)
|
setPage(page + 1)
|
||||||
reload();
|
reload().then();
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ const Header = (props: any) => {
|
|||||||
success: function () {
|
success: function () {
|
||||||
if (code) {
|
if (code) {
|
||||||
Taro.request({
|
Taro.request({
|
||||||
url: 'https://server.gxwebsoft.com/api/wx-login/loginByMpWxPhone',
|
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
code,
|
code,
|
||||||
@@ -143,7 +143,7 @@ const Header = (props: any) => {
|
|||||||
<div className={'fixed top-0 header-bg'} style={{
|
<div className={'fixed top-0 header-bg'} style={{
|
||||||
height: !props.stickyStatus ? '180px' : '148px',
|
height: !props.stickyStatus ? '180px' : '148px',
|
||||||
}}>
|
}}>
|
||||||
<MySearch done={reload}/>
|
<MySearch/>
|
||||||
{/*{!props.stickyStatus && <MySearch done={reload}/>}*/}
|
{/*{!props.stickyStatus && <MySearch done={reload}/>}*/}
|
||||||
</div>
|
</div>
|
||||||
<NavBar
|
<NavBar
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ const Login = (props:any) => {
|
|||||||
success: function () {
|
success: function () {
|
||||||
if (code) {
|
if (code) {
|
||||||
Taro.request({
|
Taro.request({
|
||||||
url: 'https://server.gxwebsoft.com/api/wx-login/loginByMpWxPhone',
|
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
code,
|
code,
|
||||||
|
|||||||
@@ -2,7 +2,8 @@ import {Search} from '@nutui/icons-react-taro'
|
|||||||
import {Button, Input} from '@nutui/nutui-react-taro'
|
import {Button, Input} from '@nutui/nutui-react-taro'
|
||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import Taro from '@tarojs/taro';
|
import Taro from '@tarojs/taro';
|
||||||
function MySearch(props) {
|
|
||||||
|
function MySearch() {
|
||||||
const [keywords, setKeywords] = useState<string>('')
|
const [keywords, setKeywords] = useState<string>('')
|
||||||
|
|
||||||
const onKeywords = (keywords: string) => {
|
const onKeywords = (keywords: string) => {
|
||||||
@@ -10,14 +11,24 @@ function MySearch(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onQuery = () => {
|
const onQuery = () => {
|
||||||
if(!keywords){
|
if(!keywords.trim()){
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请输入关键字',
|
title: '请输入关键字',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
props.done(keywords);
|
// 跳转到搜索页面
|
||||||
|
Taro.navigateTo({
|
||||||
|
url: `/shop/search/index?keywords=${encodeURIComponent(keywords.trim())}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击搜索框跳转到搜索页面
|
||||||
|
const onInputFocus = () => {
|
||||||
|
Taro.navigateTo({
|
||||||
|
url: '/shop/search/index'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -40,6 +51,7 @@ function MySearch(props) {
|
|||||||
value={keywords}
|
value={keywords}
|
||||||
onChange={onKeywords}
|
onChange={onKeywords}
|
||||||
onConfirm={onQuery}
|
onConfirm={onQuery}
|
||||||
|
onFocus={onInputFocus}
|
||||||
style={{ padding: '9px 8px'}}
|
style={{ padding: '9px 8px'}}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -7,13 +7,10 @@ import {
|
|||||||
Cell,
|
Cell,
|
||||||
CellGroup,
|
CellGroup,
|
||||||
Radio,
|
Radio,
|
||||||
Space,
|
Space
|
||||||
DatePicker,
|
|
||||||
Picker
|
|
||||||
} from '@nutui/nutui-react-taro';
|
} from '@nutui/nutui-react-taro';
|
||||||
import { Search, Filter, Close } from '@nutui/icons-react-taro';
|
import { Search, Filter, Close } from '@nutui/icons-react-taro';
|
||||||
import { ShopOrderParam } from '@/api/shop/shopOrder/model';
|
import { ShopOrderParam } from '@/api/shop/shopOrder/model';
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import './OrderSearch.scss';
|
import './OrderSearch.scss';
|
||||||
|
|
||||||
interface OrderSearchProps {
|
interface OrderSearchProps {
|
||||||
@@ -94,14 +91,14 @@ const OrderSearch: React.FC<OrderSearchProps> = ({ onSearch, onReset }) => {
|
|||||||
if (searchParams.phone?.trim()) {
|
if (searchParams.phone?.trim()) {
|
||||||
filterParams.phone = searchParams.phone.trim();
|
filterParams.phone = searchParams.phone.trim();
|
||||||
}
|
}
|
||||||
if (searchParams.orderStatus !== undefined && searchParams.orderStatus !== '') {
|
if (searchParams.orderStatus !== undefined) {
|
||||||
filterParams.orderStatus = Number(searchParams.orderStatus);
|
filterParams.orderStatus = searchParams.orderStatus;
|
||||||
}
|
}
|
||||||
if (searchParams.payStatus !== undefined && searchParams.payStatus !== '') {
|
if (searchParams.payStatus !== undefined) {
|
||||||
filterParams.payStatus = Number(searchParams.payStatus);
|
filterParams.payStatus = searchParams.payStatus;
|
||||||
}
|
}
|
||||||
if (searchParams.payType !== undefined && searchParams.payType !== '') {
|
if (searchParams.payType !== undefined) {
|
||||||
filterParams.payType = Number(searchParams.payType);
|
filterParams.payType = searchParams.payType;
|
||||||
}
|
}
|
||||||
|
|
||||||
onSearch(filterParams);
|
onSearch(filterParams);
|
||||||
@@ -186,7 +183,7 @@ const OrderSearch: React.FC<OrderSearchProps> = ({ onSearch, onReset }) => {
|
|||||||
<View className="text-sm text-gray-600 mb-2">订单状态</View>
|
<View className="text-sm text-gray-600 mb-2">订单状态</View>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
value={searchParams.orderStatus}
|
value={searchParams.orderStatus}
|
||||||
onChange={(value) => setSearchParams(prev => ({ ...prev, orderStatus: value }))}
|
onChange={(value) => setSearchParams(prev => ({ ...prev, orderStatus: Number(value) }))}
|
||||||
>
|
>
|
||||||
<View className="grid grid-cols-2 gap-2">
|
<View className="grid grid-cols-2 gap-2">
|
||||||
{orderStatusOptions.map((option) => (
|
{orderStatusOptions.map((option) => (
|
||||||
@@ -205,7 +202,7 @@ const OrderSearch: React.FC<OrderSearchProps> = ({ onSearch, onReset }) => {
|
|||||||
<View className="text-sm text-gray-600 mb-2">支付状态</View>
|
<View className="text-sm text-gray-600 mb-2">支付状态</View>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
value={searchParams.payStatus}
|
value={searchParams.payStatus}
|
||||||
onChange={(value) => setSearchParams(prev => ({ ...prev, payStatus: value }))}
|
onChange={(value) => setSearchParams(prev => ({ ...prev, payStatus: Number(value) }))}
|
||||||
>
|
>
|
||||||
<View className="flex flex-wrap gap-2">
|
<View className="flex flex-wrap gap-2">
|
||||||
{payStatusOptions.map((option) => (
|
{payStatusOptions.map((option) => (
|
||||||
@@ -224,7 +221,7 @@ const OrderSearch: React.FC<OrderSearchProps> = ({ onSearch, onReset }) => {
|
|||||||
<View className="text-sm text-gray-600 mb-2">支付方式</View>
|
<View className="text-sm text-gray-600 mb-2">支付方式</View>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
value={searchParams.payType}
|
value={searchParams.payType}
|
||||||
onChange={(value) => setSearchParams(prev => ({ ...prev, payType: value }))}
|
onChange={(value) => setSearchParams(prev => ({ ...prev, payType: Number(value) }))}
|
||||||
>
|
>
|
||||||
<View className="grid grid-cols-2 gap-2">
|
<View className="grid grid-cols-2 gap-2">
|
||||||
{payTypeOptions.map((option) => (
|
{payTypeOptions.map((option) => (
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const OrderIcon = () => {
|
|||||||
success: function () {
|
success: function () {
|
||||||
if (code) {
|
if (code) {
|
||||||
Taro.request({
|
Taro.request({
|
||||||
url: 'https://server.gxwebsoft.com/api/wx-login/loginByMpWxPhone',
|
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
code,
|
code,
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ function UserCard() {
|
|||||||
success: function () {
|
success: function () {
|
||||||
if (code) {
|
if (code) {
|
||||||
Taro.request({
|
Taro.request({
|
||||||
url: 'https://server.gxwebsoft.com/api/wx-login/loginByMpWxPhone',
|
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
code,
|
code,
|
||||||
|
|||||||
33
src/shop/search/components/GoodsItem.scss
Normal file
33
src/shop/search/components/GoodsItem.scss
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
// 使用与首页相同的样式,主要依赖Tailwind CSS类名
|
||||||
|
.buy-btn {
|
||||||
|
background: linear-gradient(to right, #1cd98a, #24ca94);
|
||||||
|
border-radius: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.cart-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 20px 0 0 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.car-no {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
58
src/shop/search/components/GoodsItem.tsx
Normal file
58
src/shop/search/components/GoodsItem.tsx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import { View } from '@tarojs/components'
|
||||||
|
import { Image } from '@nutui/nutui-react-taro'
|
||||||
|
import { Share } from '@nutui/icons-react-taro'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import { ShopGoods } from '@/api/shop/shopGoods/model'
|
||||||
|
import './GoodsItem.scss'
|
||||||
|
|
||||||
|
interface GoodsItemProps {
|
||||||
|
goods: ShopGoods
|
||||||
|
}
|
||||||
|
|
||||||
|
const GoodsItem = ({ goods }: GoodsItemProps) => {
|
||||||
|
// 跳转到商品详情
|
||||||
|
const goToDetail = () => {
|
||||||
|
Taro.navigateTo({
|
||||||
|
url: `/shop/goodsDetail/index?id=${goods.goodsId}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'flex flex-col rounded-lg bg-white shadow-sm w-full mb-5'}>
|
||||||
|
<Image
|
||||||
|
src={goods.image || ''}
|
||||||
|
mode={'aspectFit'}
|
||||||
|
lazyLoad={false}
|
||||||
|
radius="10px 10px 0 0"
|
||||||
|
height="180"
|
||||||
|
onClick={goToDetail}
|
||||||
|
/>
|
||||||
|
<div className={'flex flex-col p-2 rounded-lg'}>
|
||||||
|
<div>
|
||||||
|
<div className={'car-no text-sm'}>{goods.name || goods.goodsName}</div>
|
||||||
|
<div className={'flex justify-between text-xs py-1'}>
|
||||||
|
<span className={'text-orange-500'}>{goods.comments || ''}</span>
|
||||||
|
<span className={'text-gray-400'}>已售 {goods.sales || 0}</span>
|
||||||
|
</div>
|
||||||
|
<div className={'flex justify-between items-center py-2'}>
|
||||||
|
<div className={'flex text-red-500 text-xl items-baseline'}>
|
||||||
|
<span className={'text-xs'}>¥</span>
|
||||||
|
<span className={'font-bold text-2xl'}>{goods.price || '0.00'}</span>
|
||||||
|
</div>
|
||||||
|
<div className={'buy-btn'}>
|
||||||
|
<div className={'cart-icon'}>
|
||||||
|
<Share size={20} className={'mx-4 mt-2'}
|
||||||
|
onClick={goToDetail}/>
|
||||||
|
</div>
|
||||||
|
<div className={'text-white pl-4 pr-5'}
|
||||||
|
onClick={goToDetail}>购买
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GoodsItem
|
||||||
3
src/shop/search/index.config.ts
Normal file
3
src/shop/search/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '商品搜索'
|
||||||
|
})
|
||||||
103
src/shop/search/index.scss
Normal file
103
src/shop/search/index.scss
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
.search-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f5f5f5;
|
||||||
|
|
||||||
|
// 搜索输入框样式
|
||||||
|
.search-input-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 0 12px;
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
color: #999;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
padding: 0 16px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 18px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-content {
|
||||||
|
padding-top: calc(32px + env(safe-area-inset-top));
|
||||||
|
|
||||||
|
.search-history {
|
||||||
|
background: #fff;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.history-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px;
|
||||||
|
border-bottom: 1px solid #f5f5f5;
|
||||||
|
|
||||||
|
.history-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-btn {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-list {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.history-item {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 16px;
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #e5e5e5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results {
|
||||||
|
.result-header {
|
||||||
|
padding: 16px;
|
||||||
|
color: #666;
|
||||||
|
background: #fff;
|
||||||
|
border-bottom: 1px solid #f5f5f5;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 40px 0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
237
src/shop/search/index.tsx
Normal file
237
src/shop/search/index.tsx
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
import {useEffect, useState} from 'react'
|
||||||
|
import {useRouter} from '@tarojs/taro'
|
||||||
|
import Taro from '@tarojs/taro'
|
||||||
|
import {View} from '@tarojs/components'
|
||||||
|
import {Loading, Empty, InfiniteLoading, Input, Button} from '@nutui/nutui-react-taro'
|
||||||
|
import {Search} from '@nutui/icons-react-taro';
|
||||||
|
import {ShopGoods} from '@/api/shop/shopGoods/model'
|
||||||
|
import {pageShopGoods} from '@/api/shop/shopGoods'
|
||||||
|
import GoodsItem from './components/GoodsItem'
|
||||||
|
import './index.scss'
|
||||||
|
|
||||||
|
const SearchPage = () => {
|
||||||
|
const router = useRouter()
|
||||||
|
const [keywords, setKeywords] = useState<string>('')
|
||||||
|
const [goodsList, setGoodsList] = useState<ShopGoods[]>([])
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [page, setPage] = useState(1)
|
||||||
|
const [hasMore, setHasMore] = useState(true)
|
||||||
|
const [total, setTotal] = useState(0)
|
||||||
|
const [searchHistory, setSearchHistory] = useState<string[]>([])
|
||||||
|
|
||||||
|
// 从路由参数获取搜索关键词
|
||||||
|
useEffect(() => {
|
||||||
|
const {keywords: routeKeywords} = router.params || {}
|
||||||
|
if (routeKeywords) {
|
||||||
|
setKeywords(decodeURIComponent(routeKeywords))
|
||||||
|
handleSearch(decodeURIComponent(routeKeywords), 1).then()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载搜索历史
|
||||||
|
loadSearchHistory()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// 加载搜索历史
|
||||||
|
const loadSearchHistory = () => {
|
||||||
|
try {
|
||||||
|
const history = Taro.getStorageSync('search_history') || []
|
||||||
|
setSearchHistory(history)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载搜索历史失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存搜索历史
|
||||||
|
const saveSearchHistory = (keyword: string) => {
|
||||||
|
try {
|
||||||
|
let history = Taro.getStorageSync('search_history') || []
|
||||||
|
// 去重并添加到开头
|
||||||
|
history = history.filter(item => item !== keyword)
|
||||||
|
history.unshift(keyword)
|
||||||
|
// 只保留最近10条
|
||||||
|
history = history.slice(0, 10)
|
||||||
|
Taro.setStorageSync('search_history', history)
|
||||||
|
setSearchHistory(history)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('保存搜索历史失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleKeywords = (keywords) => {
|
||||||
|
setKeywords(keywords)
|
||||||
|
handleSearch(keywords).then()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索商品
|
||||||
|
const handleSearch = async (searchKeywords: string, pageNum: number = 1) => {
|
||||||
|
if (!searchKeywords.trim()) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请输入搜索关键词',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const params = {
|
||||||
|
keywords: searchKeywords.trim(),
|
||||||
|
page: pageNum,
|
||||||
|
size: 10,
|
||||||
|
isShow: 1 // 只搜索上架商品
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await pageShopGoods(params)
|
||||||
|
|
||||||
|
if (pageNum === 1) {
|
||||||
|
setGoodsList(result?.list || [])
|
||||||
|
setTotal(result?.count || 0)
|
||||||
|
// 保存搜索历史
|
||||||
|
saveSearchHistory(searchKeywords.trim())
|
||||||
|
} else {
|
||||||
|
setGoodsList(prev => [...prev, ...(result?.list || [])])
|
||||||
|
}
|
||||||
|
|
||||||
|
setHasMore((result?.list?.length || 0) >= 10)
|
||||||
|
setPage(pageNum)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索失败:', error)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '搜索失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载更多
|
||||||
|
const loadMore = () => {
|
||||||
|
if (!loading && hasMore && keywords.trim()) {
|
||||||
|
handleSearch(keywords, page + 1).then()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击历史搜索
|
||||||
|
const onHistoryClick = (keyword: string) => {
|
||||||
|
setKeywords(keyword)
|
||||||
|
setPage(1)
|
||||||
|
handleSearch(keyword, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空搜索历史
|
||||||
|
const clearHistory = () => {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '提示',
|
||||||
|
content: '确定要清空搜索历史吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
try {
|
||||||
|
Taro.removeStorageSync('search_history')
|
||||||
|
setSearchHistory([])
|
||||||
|
} catch (error) {
|
||||||
|
console.error('清空搜索历史失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="search-page pt-3">
|
||||||
|
<div className={'px-2'}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
background: '#ffffff',
|
||||||
|
padding: '0 5px',
|
||||||
|
borderRadius: '20px',
|
||||||
|
marginTop: '5px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Search size={18} className={'ml-2 text-gray-400'}/>
|
||||||
|
<Input
|
||||||
|
placeholder="搜索商品"
|
||||||
|
value={keywords}
|
||||||
|
onChange={handleKeywords}
|
||||||
|
onConfirm={() => handleSearch(keywords)}
|
||||||
|
style={{padding: '9px 8px'}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className={'flex items-center'}
|
||||||
|
>
|
||||||
|
<Button type="success" style={{background: 'linear-gradient(to bottom, #1cd98a, #24ca94)'}}
|
||||||
|
onClick={() => handleSearch(keywords)}>
|
||||||
|
搜索
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/*<SearchBar style={{height: `${statusBarHeight}px`}} shape="round" placeholder="搜索商品" onChange={setKeywords} onSearch={handleSearch}/>*/}
|
||||||
|
|
||||||
|
{/* 搜索内容 */}
|
||||||
|
<View className="search-content">
|
||||||
|
{/* 搜索历史 */}
|
||||||
|
{!keywords && searchHistory.length > 0 && (
|
||||||
|
<View className="search-history">
|
||||||
|
<View className="history-header">
|
||||||
|
<View className="text-sm">搜索历史</View>
|
||||||
|
<View className={'text-gray-400'} onClick={clearHistory}>清空</View>
|
||||||
|
</View>
|
||||||
|
<View className="history-list">
|
||||||
|
{searchHistory.map((item, index) => (
|
||||||
|
<View
|
||||||
|
key={index}
|
||||||
|
className="history-item"
|
||||||
|
onClick={() => onHistoryClick(item)}
|
||||||
|
>
|
||||||
|
{item}
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 搜索结果 */}
|
||||||
|
{keywords && (
|
||||||
|
<View className="search-results">
|
||||||
|
{/* 结果统计 */}
|
||||||
|
<View className="result-header">
|
||||||
|
找到 {total} 件相关商品
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* 商品列表 */}
|
||||||
|
{loading && page === 1 ? (
|
||||||
|
<View className="loading-wrapper">
|
||||||
|
<Loading>搜索中...</Loading>
|
||||||
|
</View>
|
||||||
|
) : goodsList.length > 0 ? (
|
||||||
|
<div className={'py-3'}>
|
||||||
|
<div className={'flex flex-col justify-between items-center rounded-lg px-2'}>
|
||||||
|
<InfiniteLoading
|
||||||
|
hasMore={hasMore}
|
||||||
|
// @ts-ignore
|
||||||
|
onLoadMore={loadMore}
|
||||||
|
loadingText="加载中..."
|
||||||
|
loadMoreText="没有更多了"
|
||||||
|
>
|
||||||
|
{goodsList.map((item) => (
|
||||||
|
<GoodsItem key={item.goodsId} goods={item}/>
|
||||||
|
))}
|
||||||
|
</InfiniteLoading>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Empty description="暂无相关商品"/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SearchPage
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import {useEffect, useState} from "react";
|
import {useEffect, useState} from "react";
|
||||||
import {CmsArticle} from "@/api/cms/cmsArticle/model";
|
import Taro from '@tarojs/taro';
|
||||||
import {listCmsArticle} from "@/api/cms/cmsArticle";
|
import {listCmsArticle} from "@/api/cms/cmsArticle";
|
||||||
import {Avatar, Cell, Divider} from '@nutui/nutui-react-taro'
|
import {Avatar, Cell, Divider} from '@nutui/nutui-react-taro'
|
||||||
import {ArrowRight} from '@nutui/icons-react-taro'
|
import {ArrowRight} from '@nutui/icons-react-taro'
|
||||||
@@ -7,59 +7,57 @@ import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
|
|||||||
import {listCmsNavigation} from "@/api/cms/cmsNavigation";
|
import {listCmsNavigation} from "@/api/cms/cmsNavigation";
|
||||||
// 显示html富文本
|
// 显示html富文本
|
||||||
import {View, RichText} from '@tarojs/components'
|
import {View, RichText} from '@tarojs/components'
|
||||||
|
import {listCmsDesign} from "@/api/cms/cmsDesign";
|
||||||
|
import {CmsDesign} from "@/api/cms/cmsDesign/model";
|
||||||
|
import {type Config} from "@/api/cms/cmsWebsiteField/model";
|
||||||
|
import {configWebsiteField} from "@/api/cms/cmsWebsiteField";
|
||||||
|
|
||||||
|
|
||||||
const Helper = () => {
|
const Helper = () => {
|
||||||
const [list, setList] = useState<CmsArticle[]>([])
|
|
||||||
const [nav, setNav] = useState<CmsNavigation>()
|
const [nav, setNav] = useState<CmsNavigation>()
|
||||||
|
const [design, setDesign] = useState<CmsDesign>()
|
||||||
const [category, setCategory] = useState<CmsNavigation[]>([])
|
const [category, setCategory] = useState<CmsNavigation[]>([])
|
||||||
|
const [config, setConfig] = useState<Config>()
|
||||||
|
|
||||||
const reload = async () => {
|
const reload = async () => {
|
||||||
const navs = await listCmsNavigation({model: 'page', parentId: 0});
|
const navs = await listCmsNavigation({model: 'page', parentId: 0});
|
||||||
if (navs.length > 0) {
|
if (navs.length > 0) {
|
||||||
const nav = navs[0];
|
const nav = navs[0];
|
||||||
setNav(nav);
|
setNav(nav);
|
||||||
|
// 查询页面信息
|
||||||
|
const design = await listCmsDesign({categoryId: nav.navigationId})
|
||||||
|
setDesign(design[0])
|
||||||
// 查询子栏目
|
// 查询子栏目
|
||||||
const category = await listCmsNavigation({parentId: nav.navigationId})
|
const category = await listCmsNavigation({parentId: nav.navigationId})
|
||||||
|
category.map(async (item, index) => {
|
||||||
|
category[index].articles = await listCmsArticle({categoryId: item.navigationId});
|
||||||
|
})
|
||||||
setCategory(category)
|
setCategory(category)
|
||||||
|
// 查询字段
|
||||||
|
const configInfo = await configWebsiteField({})
|
||||||
|
setConfig(configInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
listCmsArticle({model: 'page'}).then(res => {
|
|
||||||
setList(res)
|
|
||||||
}).catch(error => {
|
|
||||||
console.error("Failed to fetch goods detail:", error);
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reload().then()
|
reload().then()
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (list.length == 0) {
|
|
||||||
return (
|
|
||||||
<div className={'text-center'}>
|
|
||||||
<View className={'text-gray-500'}>
|
|
||||||
暂无数据
|
|
||||||
</View>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'px-3'}>
|
<div className={'px-3'}>
|
||||||
<Cell>
|
<Cell>
|
||||||
{nav && (
|
{nav && (
|
||||||
<View className={'flex flex-col justify-center items-center w-full'}>
|
<View className={'flex flex-col justify-center items-center w-full'}>
|
||||||
<Avatar
|
<Avatar
|
||||||
src={nav?.icon}
|
src={design?.photo}
|
||||||
size={'100'}
|
size={'100'}
|
||||||
/>
|
/>
|
||||||
<View className={'font-bold text-sm'}>
|
<View className={'font-bold text-sm'}>
|
||||||
{nav?.comments}
|
{design?.comments}
|
||||||
</View>
|
</View>
|
||||||
<View className={'text-left py-3 text-gray-600'}>
|
<View className={'text-left py-3 text-gray-600'}>
|
||||||
<RichText
|
<RichText
|
||||||
nodes={'时里院子市集是一家B2C模式的会员电商平台,以健康安全食品,中国国家地理标志产品,药食同源产品等为主线,愿景是守护5亿家庭餐桌健康;选品原则是健康,好吃,方便,实惠。通过严格的筛选供货商,从选品、采购发货、仓储物流产品检测、售前售后等各环节严格把控,提升用户满意度。平台采取透明公开化,以持续为用户提供物美价廉的好产品为准准则,为用户创造价值为唯一宗旨,倡导终身用户,终身服务的经营理念。'}/>
|
nodes={design?.content || '关于我们的简单描述'}/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
@@ -74,22 +72,21 @@ const Helper = () => {
|
|||||||
description={(
|
description={(
|
||||||
<>
|
<>
|
||||||
<Divider/>
|
<Divider/>
|
||||||
<View className={'item flex justify-between items-center my-2'}>
|
{item.articles?.map((child, _) => (
|
||||||
<View>{item.categoryName}</View>
|
<View className={'item flex justify-between items-center my-2'}>
|
||||||
<ArrowRight size={16} className={'text-gray-400'}/>
|
<View
|
||||||
</View>
|
onClick={() => Taro.navigateTo({url: `/cms/detail/index?id=${child.articleId}`})}>{child.title}</View>
|
||||||
<View className={'item flex justify-between items-center my-2'}>
|
<ArrowRight size={16} className={'text-gray-400'}/>
|
||||||
<View>{item.categoryName}</View>
|
</View>
|
||||||
<ArrowRight size={16} className={'text-gray-400'}/>
|
))}
|
||||||
</View>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
</Cell>
|
</Cell>
|
||||||
))}
|
))}
|
||||||
<Cell className={'flex flex-col'}>
|
<Cell className={'flex flex-col'}>
|
||||||
<span>服务热线:0771-88888888</span>
|
<span>服务热线:{config?.tel}</span>
|
||||||
<span>工作日:9:00-18:00</span>
|
<span>工作日:{config?.workDay}</span>
|
||||||
</Cell>
|
</Cell>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const Helper = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SearchBar shape="round" maxLength={5} className={'mt-2'} />
|
<SearchBar shape="round" className={'mt-2'} />
|
||||||
{navigation && (
|
{navigation && (
|
||||||
<Image
|
<Image
|
||||||
src={navigation.icon}
|
src={navigation.icon}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ function Profile() {
|
|||||||
avatar: `${detail.avatarUrl}`,
|
avatar: `${detail.avatarUrl}`,
|
||||||
})
|
})
|
||||||
Taro.uploadFile({
|
Taro.uploadFile({
|
||||||
url: 'https://server.gxwebsoft.com/api/oss/upload',
|
url: 'https://server.websoft.top/api/oss/upload',
|
||||||
filePath: detail.avatarUrl,
|
filePath: detail.avatarUrl,
|
||||||
name: 'file',
|
name: 'file',
|
||||||
header: {
|
header: {
|
||||||
|
|||||||
11
start-claude.sh
Executable file
11
start-claude.sh
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# 设置代理(根据你的 Clash Verge 配置)
|
||||||
|
export http_proxy=http://127.0.0.1:7897
|
||||||
|
export https_proxy=http://127.0.0.1:7897
|
||||||
|
|
||||||
|
# 启动 Claude Code
|
||||||
|
echo "🚀 启动 Claude Code..."
|
||||||
|
echo "📡 使用代理: $http_proxy"
|
||||||
|
|
||||||
|
npx @anthropic-ai/claude-code
|
||||||
Reference in New Issue
Block a user