|
@@ -8,12 +8,12 @@
|
|
|
<span>{{ t('上传照片') }}</span>
|
|
<span>{{ t('上传照片') }}</span>
|
|
|
<span v-if="!uploadRequired" class="optional">{{ t('(可选)') }}</span>
|
|
<span v-if="!uploadRequired" class="optional">{{ t('(可选)') }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="ai-btn">{{ t('AI 智能分析') }}</div>
|
|
|
|
|
|
|
+ <div class="ai-btn" :style="{ opacity: aiBtnDisabled ? 0.5 : 1 }" @click="handleAIAnalysis">{{ aiBtnText }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <upload ref="uploadRef" :maxCount="10" :initImgArr="displayImgArr" :before-read="beforeReadUpload"
|
|
|
|
|
- :after-read="afterReadUpload">
|
|
|
|
|
|
|
+ <upload ref="uploadRef" :maxCount="10" :initImgArr="props.initImgArr" :before-read="beforeReadUpload"
|
|
|
|
|
+ :after-read="afterReadUpload" @handleUpload="onUploadChange">
|
|
|
</upload>
|
|
</upload>
|
|
|
- <!-- <div class="upload-result">{{ t('AI识别结果:该病为该病为该病为该病为病为该病为病为该病为') }}</div> -->
|
|
|
|
|
|
|
+ <div class="upload-result" v-if="aiStr.length">{{ t('AI识别结果:') }}{{ aiStr }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
<slot name="footer"></slot>
|
|
<slot name="footer"></slot>
|
|
|
<div class="upload-action-btns">
|
|
<div class="upload-action-btns">
|
|
@@ -62,24 +62,37 @@ const emit = defineEmits(['update:show', 'cancel', 'confirm', 'reset']);
|
|
|
|
|
|
|
|
const uploadRef = ref(null);
|
|
const uploadRef = ref(null);
|
|
|
const popupInnerImgArr = ref([]);
|
|
const popupInnerImgArr = ref([]);
|
|
|
|
|
+/** 用户已上传/删除过图片后,以 popupInnerImgArr 为准,不再回退 props.initImgArr */
|
|
|
|
|
+const imgTouched = ref(false);
|
|
|
const popupInnerLoading = ref(false);
|
|
const popupInnerLoading = ref(false);
|
|
|
const uploadFileObj = new UploadFile();
|
|
const uploadFileObj = new UploadFile();
|
|
|
const miniUserId = localStorage.getItem("MINI_USER_ID");
|
|
const miniUserId = localStorage.getItem("MINI_USER_ID");
|
|
|
|
|
|
|
|
const loading = computed(() => props.popupImageUploadLoading || popupInnerLoading.value);
|
|
const loading = computed(() => props.popupImageUploadLoading || popupInnerLoading.value);
|
|
|
|
|
|
|
|
-/** 弹窗内上传优先展示,否则回显父组件传入的 initImgArr */
|
|
|
|
|
-const displayImgArr = computed(() =>
|
|
|
|
|
- popupInnerImgArr.value.length ? popupInnerImgArr.value : props.initImgArr,
|
|
|
|
|
|
|
+const aiStr = ref('');
|
|
|
|
|
+/** 已点击过 AI 分析,防止重复请求 */
|
|
|
|
|
+const aiAnalyzeLocked = ref(false);
|
|
|
|
|
+const aiBtnDisabled = computed(
|
|
|
|
|
+ () => popupInnerImgArr.value.length === 0 || aiAnalyzeLocked.value,
|
|
|
|
|
+);
|
|
|
|
|
+const aiBtnText = computed(() =>
|
|
|
|
|
+ aiAnalyzeLocked.value ? t('识别中') : t('AI 智能分析'),
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
function getConfirmImgArr() {
|
|
function getConfirmImgArr() {
|
|
|
- if (popupInnerImgArr.value.length) {
|
|
|
|
|
- return [...popupInnerImgArr.value.map(item => base_img_url2 + item)];
|
|
|
|
|
|
|
+ if (imgTouched.value || popupInnerImgArr.value.length) {
|
|
|
|
|
+ return popupInnerImgArr.value.map((item) => base_img_url2 + item);
|
|
|
}
|
|
}
|
|
|
return [...(props.initImgArr || [])];
|
|
return [...(props.initImgArr || [])];
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/** 与 upload 组件内 imgArr 同步(含删除),用于 AI 按钮透明度等 */
|
|
|
|
|
+function onUploadChange({ imgArr }) {
|
|
|
|
|
+ imgTouched.value = true;
|
|
|
|
|
+ popupInnerImgArr.value = [...(imgArr || [])];
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
const beforeReadUpload = () => {
|
|
const beforeReadUpload = () => {
|
|
|
popupInnerLoading.value = false;
|
|
popupInnerLoading.value = false;
|
|
|
return true;
|
|
return true;
|
|
@@ -101,6 +114,7 @@ const afterReadUpload = async (data) => {
|
|
|
if (resFilename) {
|
|
if (resFilename) {
|
|
|
file.status = "done";
|
|
file.status = "done";
|
|
|
file.message = "";
|
|
file.message = "";
|
|
|
|
|
+ imgTouched.value = true;
|
|
|
popupInnerImgArr.value.push(resFilename);
|
|
popupInnerImgArr.value.push(resFilename);
|
|
|
} else {
|
|
} else {
|
|
|
file.status = "failed";
|
|
file.status = "failed";
|
|
@@ -110,6 +124,7 @@ const afterReadUpload = async (data) => {
|
|
|
}
|
|
}
|
|
|
} finally {
|
|
} finally {
|
|
|
popupInnerLoading.value = false;
|
|
popupInnerLoading.value = false;
|
|
|
|
|
+ uploadRef.value?.syncImgArr?.(popupInnerImgArr.value);
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -124,8 +139,37 @@ function handleConfirm() {
|
|
|
|
|
|
|
|
function uploadReset() {
|
|
function uploadReset() {
|
|
|
popupInnerImgArr.value = [];
|
|
popupInnerImgArr.value = [];
|
|
|
|
|
+ imgTouched.value = false;
|
|
|
popupInnerLoading.value = false;
|
|
popupInnerLoading.value = false;
|
|
|
uploadRef.value?.uploadReset?.();
|
|
uploadRef.value?.uploadReset?.();
|
|
|
|
|
+ aiStr.value = '';
|
|
|
|
|
+ aiAnalyzeLocked.value = false;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function handleAIAnalysis() {
|
|
|
|
|
+ if (aiAnalyzeLocked.value) return;
|
|
|
|
|
+ const farmData = JSON.parse(localStorage.getItem('selectedFarmData'));
|
|
|
|
|
+ const imgArr = getConfirmImgArr();
|
|
|
|
|
+ if (imgArr.length === 0) return;
|
|
|
|
|
+ aiAnalyzeLocked.value = true;
|
|
|
|
|
+ const params = {
|
|
|
|
|
+ image_urls: imgArr,
|
|
|
|
|
+ crop_type: farmData.farm_variety,
|
|
|
|
|
+ };
|
|
|
|
|
+ VE_API.record.batchPestIdentify(params)
|
|
|
|
|
+ .then((res) => {
|
|
|
|
|
+ if (res.code === 200 && res.results?.length) {
|
|
|
|
|
+ aiStr.value = res.results.map((item) => item.disease_name).join(',');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ElMessage.error(t('AI识别失败,请稍后再试!'));
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ .catch(() => {
|
|
|
|
|
+ ElMessage.error(t('AI识别失败,请稍后再试!'));
|
|
|
|
|
+ })
|
|
|
|
|
+ .finally(() => {
|
|
|
|
|
+ aiAnalyzeLocked.value = false;
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
watch(
|
|
watch(
|
|
@@ -175,7 +219,6 @@ defineExpose({
|
|
|
background: rgba(33, 153, 248, 0.1);
|
|
background: rgba(33, 153, 248, 0.1);
|
|
|
color: #2199F8;
|
|
color: #2199F8;
|
|
|
border: 1px solid #2199F8;
|
|
border: 1px solid #2199F8;
|
|
|
- opacity: 0.5;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|